aboutsummaryrefslogtreecommitdiffstats
path: root/main
diff options
context:
space:
mode:
Diffstat (limited to 'main')
-rw-r--r--main/Makefile156
-rw-r--r--main/abstract_jb.c822
-rw-r--r--main/acl.c590
-rw-r--r--main/aescrypt.c317
-rw-r--r--main/aeskey.c469
-rw-r--r--main/aesopt.h1029
-rw-r--r--main/aestab.c232
-rw-r--r--main/alaw.c101
-rw-r--r--main/app.c1452
-rw-r--r--main/ast_expr2.c2859
-rw-r--r--main/ast_expr2.fl414
-rw-r--r--main/ast_expr2.h112
-rw-r--r--main/ast_expr2.y1045
-rw-r--r--main/ast_expr2f.c3349
-rw-r--r--main/asterisk.c3223
-rw-r--r--main/astmm.c498
-rw-r--r--main/astobj2.c771
-rw-r--r--main/audiohook.c721
-rw-r--r--main/autoservice.c319
-rw-r--r--main/buildinfo.c33
-rw-r--r--main/callerid.c1117
-rw-r--r--main/cdr.c1499
-rw-r--r--main/channel.c4876
-rw-r--r--main/chanvars.c87
-rw-r--r--main/cli.c2037
-rw-r--r--main/coef_in.h13
-rw-r--r--main/coef_out.h4
-rw-r--r--main/config.c1520
-rw-r--r--main/cryptostub.c95
-rw-r--r--main/db.c592
-rw-r--r--main/db1-ast/Makefile71
-rw-r--r--main/db1-ast/btree/bt_close.c182
-rw-r--r--main/db1-ast/btree/bt_conv.c221
-rw-r--r--main/db1-ast/btree/bt_debug.c329
-rw-r--r--main/db1-ast/btree/bt_delete.c657
-rw-r--r--main/db1-ast/btree/bt_get.c105
-rw-r--r--main/db1-ast/btree/bt_open.c458
-rw-r--r--main/db1-ast/btree/bt_overflow.c228
-rw-r--r--main/db1-ast/btree/bt_page.c100
-rw-r--r--main/db1-ast/btree/bt_put.c321
-rw-r--r--main/db1-ast/btree/bt_search.c213
-rw-r--r--main/db1-ast/btree/bt_seq.c460
-rw-r--r--main/db1-ast/btree/bt_split.c829
-rw-r--r--main/db1-ast/btree/bt_utils.c260
-rw-r--r--main/db1-ast/btree/btree.h391
-rw-r--r--main/db1-ast/btree/extern.h70
-rw-r--r--main/db1-ast/db/db.c103
-rw-r--r--main/db1-ast/hash/README72
-rw-r--r--main/db1-ast/hash/extern.h65
-rw-r--r--main/db1-ast/hash/hash.c999
-rw-r--r--main/db1-ast/hash/hash.h293
-rw-r--r--main/db1-ast/hash/hash_bigkey.c668
-rw-r--r--main/db1-ast/hash/hash_buf.c355
-rw-r--r--main/db1-ast/hash/hash_func.c225
-rw-r--r--main/db1-ast/hash/hash_log2.c56
-rw-r--r--main/db1-ast/hash/hash_page.c946
-rw-r--r--main/db1-ast/hash/hsearch.c107
-rw-r--r--main/db1-ast/hash/ndbm.c235
-rw-r--r--main/db1-ast/hash/page.h92
-rw-r--r--main/db1-ast/hash/search.h51
-rw-r--r--main/db1-ast/include/circ-queue.h131
-rw-r--r--main/db1-ast/include/compat.h49
-rw-r--r--main/db1-ast/include/db.h250
-rw-r--r--main/db1-ast/include/mpool.h115
-rw-r--r--main/db1-ast/include/ndbm.h79
-rw-r--r--main/db1-ast/libdb.map11
-rw-r--r--main/db1-ast/mpool/README7
-rw-r--r--main/db1-ast/mpool/mpool.c498
-rw-r--r--main/db1-ast/recno/extern.h54
-rw-r--r--main/db1-ast/recno/rec_close.c183
-rw-r--r--main/db1-ast/recno/rec_delete.c197
-rw-r--r--main/db1-ast/recno/rec_get.c311
-rw-r--r--main/db1-ast/recno/rec_open.c241
-rw-r--r--main/db1-ast/recno/rec_put.c280
-rw-r--r--main/db1-ast/recno/rec_search.c126
-rw-r--r--main/db1-ast/recno/rec_seq.c131
-rw-r--r--main/db1-ast/recno/rec_utils.c122
-rw-r--r--main/db1-ast/recno/recno.h39
-rw-r--r--main/devicestate.c369
-rw-r--r--main/dial.c894
-rw-r--r--main/dns.c292
-rw-r--r--main/dnsmgr.c424
-rw-r--r--main/dsp.c1819
-rw-r--r--main/ecdisa.h15
-rw-r--r--main/editline/CHANGES42
-rw-r--r--main/editline/INSTALL64
-rw-r--r--main/editline/Makefile.in234
-rw-r--r--main/editline/PLATFORMS13
-rw-r--r--main/editline/README11
-rw-r--r--main/editline/TEST/test.c268
-rw-r--r--main/editline/chared.c695
-rw-r--r--main/editline/chared.h159
-rw-r--r--main/editline/common.c951
-rwxr-xr-xmain/editline/config.guess1449
-rw-r--r--main/editline/config.h.in21
-rwxr-xr-xmain/editline/config.sub1412
-rwxr-xr-xmain/editline/configure2459
-rw-r--r--main/editline/configure.in278
-rw-r--r--main/editline/editline.3646
-rw-r--r--main/editline/editrc.5491
-rw-r--r--main/editline/el.c509
-rw-r--r--main/editline/el.h145
-rw-r--r--main/editline/emacs.c488
-rw-r--r--main/editline/hist.c197
-rw-r--r--main/editline/hist.h80
-rw-r--r--main/editline/histedit.h197
-rw-r--r--main/editline/history.c875
-rwxr-xr-xmain/editline/install-sh250
-rw-r--r--main/editline/key.c687
-rw-r--r--main/editline/key.h79
-rw-r--r--main/editline/makelist.in254
-rw-r--r--main/editline/map.c1418
-rw-r--r--main/editline/map.h79
-rw-r--r--main/editline/np/fgetln.c88
-rw-r--r--main/editline/np/strlcat.c75
-rw-r--r--main/editline/np/strlcpy.c75
-rw-r--r--main/editline/np/unvis.c322
-rw-r--r--main/editline/np/vis.c348
-rw-r--r--main/editline/np/vis.h96
-rw-r--r--main/editline/parse.c259
-rw-r--r--main/editline/parse.h52
-rw-r--r--main/editline/prompt.c174
-rw-r--r--main/editline/prompt.h62
-rw-r--r--main/editline/read.c558
-rw-r--r--main/editline/read.h55
-rw-r--r--main/editline/readline.c1664
-rw-r--r--main/editline/readline/readline.h118
-rw-r--r--main/editline/refresh.c1104
-rw-r--r--main/editline/refresh.h63
-rw-r--r--main/editline/search.c649
-rw-r--r--main/editline/search.h70
-rw-r--r--main/editline/sig.c198
-rw-r--r--main/editline/sig.h72
-rw-r--r--main/editline/sys.h133
-rw-r--r--main/editline/term.c1587
-rw-r--r--main/editline/term.h124
-rw-r--r--main/editline/tokenizer.c397
-rw-r--r--main/editline/tokenizer.h54
-rw-r--r--main/editline/tty.c1182
-rw-r--r--main/editline/tty.h484
-rw-r--r--main/editline/vi.c941
-rw-r--r--main/enum.c671
-rw-r--r--main/file.c1392
-rw-r--r--main/fixedjitterbuf.c351
-rw-r--r--main/fixedjitterbuf.h92
-rw-r--r--main/frame.c1607
-rw-r--r--main/fskmodem.c309
-rw-r--r--main/global_datastores.c113
-rw-r--r--main/http.c752
-rw-r--r--main/image.c225
-rw-r--r--main/indications.c597
-rw-r--r--main/io.c371
-rw-r--r--main/jitterbuf.c839
-rw-r--r--main/loader.c1001
-rw-r--r--main/logger.c939
-rw-r--r--main/manager.c3194
-rw-r--r--main/md5.c267
-rw-r--r--main/netsock.c217
-rw-r--r--main/pbx.c6401
-rw-r--r--main/plc.c251
-rw-r--r--main/poll.c306
-rw-r--r--main/privacy.c119
-rw-r--r--main/rtp.c3833
-rw-r--r--main/say.c7285
-rw-r--r--main/sched.c404
-rw-r--r--main/sha1.c385
-rw-r--r--main/slinfactory.c177
-rw-r--r--main/srv.c246
-rw-r--r--main/stdtime/Makefile29
-rw-r--r--main/stdtime/localtime.c1651
-rw-r--r--main/stdtime/private.h358
-rw-r--r--main/stdtime/test.c21
-rw-r--r--main/stdtime/tzfile.h184
-rw-r--r--main/strcompat.c472
-rw-r--r--main/tdd.c328
-rw-r--r--main/term.c303
-rw-r--r--main/threadstorage.c245
-rw-r--r--main/translate.c986
-rw-r--r--main/udptl.c1247
-rw-r--r--main/ulaw.c106
-rw-r--r--main/utils.c1414
181 files changed, 111294 insertions, 0 deletions
diff --git a/main/Makefile b/main/Makefile
new file mode 100644
index 000000000..53274ea4a
--- /dev/null
+++ b/main/Makefile
@@ -0,0 +1,156 @@
+#
+# 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
+
+OBJS= 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 astobj2.o global_datastores.o \
+ audiohook.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
+
+ifneq ($(findstring darwin,$(OSARCH)),)
+ OBJS+=poll.o
+ ASTCFLAGS+=-DPOLLCOMPAT
+else
+ ifeq ($(wildcard /usr/include/sys/poll.h),)
+ OBJS+=poll.o
+ ASTCFLAGS+=-DPOLLCOMPAT
+ endif
+endif
+
+ifneq ($(findstring $(OSARCH), linux-gnu uclinux linux-uclibc linux-gnueabi ),)
+ 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)
+ AST_LIBS+=-lcrypto
+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
+
+editline/libedit.a:
+ cd editline && test -f config.h || CFLAGS="$(PTHREAD_CFLAGS) $(subst $(ASTTOPDIR),../../,$(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:
+ CFLAGS="$(subst $(ASTTOPDIR),../../,$(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
+
+ast_expr2f.o: ASTCFLAGS+=-Wno-unused
+
+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
+ rm ast_expr2.o ast_expr2f.o
+
+asterisk.o channel.o: ASTCFLAGS+=$(ZAPTEL_INCLUDE) $(DAHDI_INCLUDE)
+
+stdtime/localtime.o: ASTCFLAGS+=$(AST_NO_STRICT_OVERFLOW)
+
+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
+
+asterisk: $(OBJS) editline/libedit.a db1-ast/libdb1.a $(AST_EMBED_LDSCRIPTS)
+ @$(ASTTOPDIR)/build_tools/make_build_h > $(ASTTOPDIR)/include/asterisk/build.h.tmp
+ @if cmp -s $(ASTTOPDIR)/include/asterisk/build.h.tmp $(ASTTOPDIR)/include/asterisk/build.h ; then echo ; else \
+ mv $(ASTTOPDIR)/include/asterisk/build.h.tmp $(ASTTOPDIR)/include/asterisk/build.h ; \
+ fi
+ @rm -f $(ASTTOPDIR)/include/asterisk/build.h.tmp
+ @$(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
+ $(CMD_PREFIX) $(ASTTOPDIR)/build_tools/strip_nonapi $@ || rm $@
+
+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
diff --git a/main/abstract_jb.c b/main/abstract_jb.c
new file mode 100644
index 000000000..226df462b
--- /dev/null
+++ b/main/abstract_jb.c
@@ -0,0 +1,822 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/logger.h"
+#include "asterisk/term.h"
+#include "asterisk/options.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 Empty and reset jb */
+typedef void (*jb_empty_and_reset_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;
+ jb_empty_and_reset_impl empty_and_reset;
+};
+
+/* 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);
+static void jb_empty_and_reset_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);
+static void jb_empty_and_reset_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,
+ .empty_and_reset = jb_empty_and_reset_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,
+ .empty_and_reset = jb_empty_and_reset_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 recieved 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");
+ ast_assert("JB type unknown" == NULL);
+ 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);
+ /* We should always have bridged chan if a jitterbuffer is in use */
+ ast_assert(bridged != NULL);
+
+ 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);
+ }
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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);
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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, char *varname, char *value)
+{
+ int prefixlen = sizeof(AST_JB_CONF_PREFIX) - 1;
+ 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));
+}
+
+void ast_jb_empty_and_reset(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 && jb0->impl->empty_and_reset) {
+ jb0->impl->empty_and_reset(jb0->jbobj);
+ }
+
+ if (c1_use_jb && c1_jb_is_created && jb1->impl->empty_and_reset) {
+ jb1->impl->empty_and_reset(jb1->jbobj);
+ }
+}
+
+/* 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);
+}
+
+static void jb_empty_and_reset_fixed(void *jb)
+{
+ struct fixed_jb *fixedjb = jb;
+ struct fixed_jb_frame f;
+
+ while (fixed_jb_remove(fixedjb, &f) == FIXED_JB_OK) {
+ ast_frfree(f.data);
+ }
+}
+
+/* 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)
+{
+}
+
+static void jb_empty_and_reset_adaptive(void *jb)
+{
+ jitterbuf *adaptivejb = jb;
+ jb_frame f;
+
+ while (jb_getall(adaptivejb, &f) == JB_OK) {
+ ast_frfree(f.data);
+ }
+
+ jb_reset(adaptivejb);
+}
diff --git a/main/acl.c b/main/acl.c
new file mode 100644
index 000000000..4a5e16485
--- /dev/null
+++ b/main/acl.c
@@ -0,0 +1,590 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/ioctl.h>
+
+#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__Darwin__)
+#include <fcntl.h>
+#include <net/route.h>
+#endif
+
+#if defined(SOLARIS)
+#include <sys/sockio.h>
+#include <net/if.h>
+#elif defined(HAVE_GETIFADDRS)
+#include <ifaddrs.h>
+#endif
+
+/* netinet/ip.h may not define the following (See RFCs 791 and 1349) */
+#if !defined(IPTOS_LOWCOST)
+#define IPTOS_LOWCOST 0x02
+#endif
+
+#if !defined(IPTOS_MINCOST)
+#define IPTOS_MINCOST IPTOS_LOWCOST
+#endif
+
+#include "asterisk/acl.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/srv.h"
+
+struct ast_ha {
+ /* Host access rule */
+ struct in_addr netaddr;
+ struct in_addr netmask;
+ int sense;
+ struct ast_ha *next;
+};
+
+/* Default IP - if not otherwise set, don't breathe garbage */
+static struct in_addr __ourip = { .s_addr = 0x00000000, };
+
+struct my_ifreq {
+ char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "eth0", "ppp0", etc. */
+ struct sockaddr_in ifru_addr;
+};
+
+#if (!defined(SOLARIS) && !defined(HAVE_GETIFADDRS))
+static int get_local_address(struct in_addr *ourip)
+{
+ return -1;
+}
+#else
+static void score_address(const struct sockaddr_in *sin, struct in_addr *best_addr, int *best_score)
+{
+ const char *address;
+ int score;
+
+ address = ast_inet_ntoa(sin->sin_addr);
+
+ /* RFC 1700 alias for the local network */
+ if (address[0] == '0')
+ score = -25;
+ /* RFC 1700 localnet */
+ else if (strncmp(address, "127", 3) == 0)
+ score = -20;
+ /* RFC 1918 non-public address space */
+ else if (strncmp(address, "10.", 3) == 0)
+ score = -5;
+ /* RFC 1918 non-public address space */
+ else if (strncmp(address, "172", 3) == 0) {
+ /* 172.16.0.0 - 172.19.255.255, but not 172.160.0.0 - 172.169.255.255 */
+ if (address[4] == '1' && address[5] >= '6' && address[6] == '.')
+ score = -5;
+ /* 172.20.0.0 - 172.29.255.255, but not 172.200.0.0 - 172.255.255.255 nor 172.2.0.0 - 172.2.255.255 */
+ else if (address[4] == '2' && address[6] == '.')
+ score = -5;
+ /* 172.30.0.0 - 172.31.255.255 */
+ else if (address[4] == '3' && address[5] <= '1')
+ score = -5;
+ /* All other 172 addresses are public */
+ else
+ score = 0;
+ /* RFC 2544 Benchmark test range */
+ } else if (strncmp(address, "198.1", 5) == 0 && address[5] >= '8' && address[6] == '.')
+ score = -10;
+ /* RFC 1918 non-public address space */
+ else if (strncmp(address, "192.168", 7) == 0)
+ score = -5;
+ /* RFC 3330 Zeroconf network */
+ else if (strncmp(address, "169.254", 7) == 0)
+ /*!\note Better score than a test network, but not quite as good as RFC 1918
+ * address space. The reason is that some Linux distributions automatically
+ * configure a Zeroconf address before trying DHCP, so we want to prefer a
+ * DHCP lease to a Zeroconf address.
+ */
+ score = -10;
+ /* RFC 3330 Test network */
+ else if (strncmp(address, "192.0.2.", 8) == 0)
+ score = -15;
+ /* Every other address should be publically routable */
+ else
+ score = 0;
+
+ if (score > *best_score) {
+ *best_score = score;
+ memcpy(best_addr, &sin->sin_addr, sizeof(*best_addr));
+ }
+}
+
+static int get_local_address(struct in_addr *ourip)
+{
+ int s, res = -1;
+#ifdef SOLARIS
+ struct lifreq *ifr = NULL;
+ struct lifnum ifn;
+ struct lifconf ifc;
+ struct sockaddr_in *sa;
+ char *buf = NULL;
+ int bufsz, x;
+#endif /* SOLARIS */
+#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__)
+ struct ifaddrs *ifap, *ifaphead;
+ int rtnerr;
+ const struct sockaddr_in *sin;
+#endif /* BSD_OR_LINUX */
+ struct in_addr best_addr;
+ int best_score = -100;
+ memset(&best_addr, 0, sizeof(best_addr));
+
+#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__)
+ rtnerr = getifaddrs(&ifaphead);
+ if (rtnerr) {
+ perror(NULL);
+ return -1;
+ }
+#endif /* BSD_OR_LINUX */
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (s > 0) {
+#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__)
+ for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
+
+ if (ifap->ifa_addr && ifap->ifa_addr->sa_family == AF_INET) {
+ sin = (const struct sockaddr_in *) ifap->ifa_addr;
+ score_address(sin, &best_addr, &best_score);
+ res = 0;
+
+ if (best_score == 0)
+ break;
+ }
+ }
+#endif /* BSD_OR_LINUX */
+
+ /* There is no reason whatsoever that this shouldn't work on Linux or BSD also. */
+#ifdef SOLARIS
+ /* Get a count of interfaces on the machine */
+ ifn.lifn_family = AF_INET;
+ ifn.lifn_flags = 0;
+ ifn.lifn_count = 0;
+ if (ioctl(s, SIOCGLIFNUM, &ifn) < 0) {
+ close(s);
+ return -1;
+ }
+
+ bufsz = ifn.lifn_count * sizeof(struct lifreq);
+ if (!(buf = malloc(bufsz))) {
+ close(s);
+ return -1;
+ }
+ memset(buf, 0, bufsz);
+
+ /* Get a list of interfaces on the machine */
+ ifc.lifc_len = bufsz;
+ ifc.lifc_buf = buf;
+ ifc.lifc_family = AF_INET;
+ ifc.lifc_flags = 0;
+ if (ioctl(s, SIOCGLIFCONF, &ifc) < 0) {
+ close(s);
+ free(buf);
+ return -1;
+ }
+
+ for (ifr = ifc.lifc_req, x = 0; x < ifn.lifn_count; ifr++, x++) {
+ sa = (struct sockaddr_in *)&(ifr->lifr_addr);
+ score_address(sa, &best_addr, &best_score);
+ res = 0;
+
+ if (best_score == 0)
+ break;
+ }
+
+ free(buf);
+#endif /* SOLARIS */
+
+ close(s);
+ }
+#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__)
+ freeifaddrs(ifaphead);
+#endif /* BSD_OR_LINUX */
+
+ if (res == 0 && ourip)
+ memcpy(ourip, &best_addr, sizeof(*ourip));
+ return res;
+}
+#endif /* HAVE_GETIFADDRS */
+
+/* Free HA structure */
+void ast_free_ha(struct ast_ha *ha)
+{
+ struct ast_ha *hal;
+ while (ha) {
+ hal = ha;
+ ha = ha->next;
+ 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(char *sense, char *stuff, struct ast_ha *path)
+{
+ struct ast_ha *ha;
+ char *nm = "255.255.255.255";
+ char tmp[256];
+ struct ast_ha *prev = NULL;
+ struct ast_ha *ret;
+ int x, z;
+ unsigned int y;
+
+ ret = path;
+ while (path) {
+ prev = path;
+ path = path->next;
+ }
+ if ((ha = ast_malloc(sizeof(*ha)))) {
+ ast_copy_string(tmp, stuff, sizeof(tmp));
+ nm = strchr(tmp, '/');
+ if (!nm) {
+ nm = "255.255.255.255";
+ } else {
+ *nm = '\0';
+ nm++;
+ }
+ if (!strchr(nm, '.')) {
+ if ((sscanf(nm, "%d", &x) == 1) && (x >= 0) && (x <= 32)) {
+ y = 0;
+ for (z = 0; z < x; z++) {
+ y >>= 1;
+ y |= 0x80000000;
+ }
+ ha->netmask.s_addr = htonl(y);
+ }
+ } else if (!inet_aton(nm, &ha->netmask)) {
+ ast_log(LOG_WARNING, "%s is not a valid netmask\n", nm);
+ free(ha);
+ return ret;
+ }
+ if (!inet_aton(tmp, &ha->netaddr)) {
+ ast_log(LOG_WARNING, "%s is not a valid IP\n", tmp);
+ free(ha);
+ return ret;
+ }
+ ha->netaddr.s_addr &= ha->netmask.s_addr;
+ if (!strncasecmp(sense, "p", 1)) {
+ ha->sense = AST_SENSE_ALLOW;
+ } else {
+ ha->sense = AST_SENSE_DENY;
+ }
+ ha->next = NULL;
+ if (prev) {
+ prev->next = ha;
+ } else {
+ ret = ha;
+ }
+ }
+ if (option_debug)
+ ast_log(LOG_DEBUG, "%s/%s appended to acl for peer\n", stuff, nm);
+ return ret;
+}
+
+int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin)
+{
+ /* Start optimistic */
+ int res = AST_SENSE_ALLOW;
+ while (ha) {
+ 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));
+ if (option_debug)
+ ast_log(LOG_DEBUG, "##### Testing %s with %s\n", iabuf, iabuf2);
+ /* 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 (inet_aton(value, &sin->sin_addr))
+ return 0;
+ 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_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;
+ }
+ }
+
+ if (!strcasecmp(value, "lowdelay"))
+ *tos = IPTOS_LOWDELAY;
+ else if (!strcasecmp(value, "throughput"))
+ *tos = IPTOS_THROUGHPUT;
+ else if (!strcasecmp(value, "reliability"))
+ *tos = IPTOS_RELIABILITY;
+ else if (!strcasecmp(value, "mincost"))
+ *tos = IPTOS_MINCOST;
+ else if (!strcasecmp(value, "none"))
+ *tos = 0;
+ else
+ return -1;
+
+ ast_log(LOG_WARNING, "TOS value %s is deprecated. Please see doc/ip-tos.txt for more information.\n", value);
+
+ return 0;
+}
+
+const char *ast_tos2str(unsigned int tos)
+{
+ unsigned int x;
+
+ switch (tos) {
+ case 0:
+ return "none";
+ case IPTOS_LOWDELAY:
+ return "lowdelay";
+ case IPTOS_THROUGHPUT:
+ return "throughput";
+ case IPTOS_RELIABILITY:
+ return "reliability";
+ case IPTOS_MINCOST:
+ return "mincost";
+ default:
+ 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);
+}
+
+/* iface is the interface (e.g. eth0); address is the return value */
+int ast_lookup_iface(char *iface, struct in_addr *address)
+{
+ int mysock, res = 0;
+ struct my_ifreq ifreq;
+
+ memset(&ifreq, 0, sizeof(ifreq));
+ ast_copy_string(ifreq.ifrn_name, iface, sizeof(ifreq.ifrn_name));
+
+ mysock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ res = ioctl(mysock, SIOCGIFADDR, &ifreq);
+
+ close(mysock);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
+ memcpy((char *)address, (char *)&__ourip, sizeof(__ourip));
+ return -1;
+ } else {
+ memcpy((char *)address, (char *)&ifreq.ifru_addr.sin_addr, sizeof(ifreq.ifru_addr.sin_addr));
+ return 0;
+ }
+}
+
+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_WARNING, "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);
+ *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));
+ 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));
+ return 0;
+ }
+ }
+ /* A.ROOT-SERVERS.NET. */
+ if (inet_aton("198.41.0.4", &saddr) && !ast_ouraddrfor(&saddr, ourip))
+ return 0;
+ return get_local_address(ourip);
+}
+
diff --git a/main/aescrypt.c b/main/aescrypt.c
new file mode 100644
index 000000000..1ccddf3f5
--- /dev/null
+++ b/main/aescrypt.c
@@ -0,0 +1,317 @@
+/*
+ ---------------------------------------------------------------------------
+ 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>
+ */
+
+#include "aesopt.h"
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#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
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/main/aeskey.c b/main/aeskey.c
new file mode 100644
index 000000000..d34badc6b
--- /dev/null
+++ b/main/aeskey.c
@@ -0,0 +1,469 @@
+/*
+ ---------------------------------------------------------------------------
+ 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>
+ */
+
+#include "aesopt.h"
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/* 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
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/main/aesopt.h b/main/aesopt.h
new file mode 100644
index 000000000..bb4f05a0b
--- /dev/null
+++ b/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/main/aestab.c b/main/aestab.c
new file mode 100644
index 000000000..c84a480af
--- /dev/null
+++ b/main/aestab.c
@@ -0,0 +1,232 @@
+/*
+ ---------------------------------------------------------------------------
+ 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
+
+#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
+
+#if defined(__cplusplus)
+}
+#endif
+
diff --git a/main/alaw.c b/main/alaw.c
new file mode 100644
index 000000000..b94772ea6
--- /dev/null
+++ b/main/alaw.c
@@ -0,0 +1,101 @@
+/*
+ * 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/alaw.h"
+
+#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;
+}
+/*- End of function --------------------------------------------------------*/
+
+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);
+}
+
+unsigned char __ast_lin2a[8192];
+short __ast_alaw[256];
+
+void ast_alaw_init(void)
+{
+ int i;
+ /*
+ * Set up mu-law conversion table
+ */
+ 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);
+ }
+
+}
+
diff --git a/main/app.c b/main/app.c
new file mode 100644
index 000000000..bcecff568
--- /dev/null
+++ b/main/app.c
@@ -0,0 +1,1452 @@
+/*
+ * 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$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <regex.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/indications.h"
+#include "asterisk/linkedlists.h"
+
+#define MAX_OTHER_FORMATS 10
+
+static AST_LIST_HEAD_STATIC(groups, ast_group_info);
+
+/* !
+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.
+\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 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;
+
+ ts = ast_get_indication_tone(chan->zone,"dial");
+ if (ts && 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;
+}
+
+/*! \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, char *prompt, char *s, int maxlen, int timeout)
+{
+ int res,to,fto;
+ /* XXX Merge with full version? XXX */
+ if (maxlen)
+ s[0] = '\0';
+ if (prompt) {
+ res = ast_streamfile(c, prompt, c->language);
+ if (res < 0)
+ return res;
+ }
+ 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;
+ res = ast_readstring(c, s, maxlen, to, fto, "#");
+ return res;
+}
+
+
+int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
+{
+ int res, to, fto;
+ if (prompt) {
+ res = ast_streamfile(c, prompt, c->language);
+ if (res < 0)
+ return res;
+ }
+ fto = 6000;
+ to = 2000;
+ 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 ((option_verbose > 2) && !warned) {
+ ast_verbose(VERBOSE_PREFIX_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 && (option_verbose > 2)) {
+ warned++;
+ ast_verbose(VERBOSE_PREFIX_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 && (option_verbose > 2)) {
+ warned++;
+ ast_verbose(VERBOSE_PREFIX_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)
+{
+ const char *ptr;
+ int res = 0;
+ struct ast_silence_generator *silgen = NULL;
+
+ 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) {
+ if (peer) {
+ ast_autoservice_stop(peer);
+ }
+ return res;
+ }
+
+ if (ast_opt_transmit_silence) {
+ silgen = ast_channel_start_silence_generator(chan);
+ }
+
+ 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);
+ /* 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;
+ }
+
+ if (silgen) {
+ ast_channel_stop_silence_generator(chan, silgen);
+ }
+
+ 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);
+ free(params);
+}
+
+static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct ast_frame f;
+ short buf[2048 + AST_FRIENDLY_OFFSET / 2];
+ struct linear_state *ls = data;
+ 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;
+ }
+ memset(&f, 0, sizeof(f));
+ res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
+ if (res > 0) {
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR;
+ f.data = buf + AST_FRIENDLY_OFFSET/2;
+ f.datalen = res;
+ f.samples = res / 2;
+ f.offset = AST_FRIENDLY_OFFSET;
+ 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;
+ /* In this case, params is already malloc'd */
+ if (params) {
+ ls = params;
+ 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);
+ 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", (char *)ast_config_AST_DATA_DIR, "sounds", filename);
+ fd = open(tmpf, O_RDONLY);
+ if (fd < 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)
+{
+ char *breaks = NULL;
+ char *end = NULL;
+ int blen = 2;
+ int res;
+ long pause_restart_point = 0;
+
+ 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) {
+ ast_seekstream(chan->stream, 0, SEEK_END);
+ end = NULL;
+ };
+ 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)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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 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;
+ d = ast_streamfile(chan, fn, chan->language);
+ if (d)
+ 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 == NULL) {
+ ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
+ return -1;
+ }
+
+ if (option_debug)
+ ast_log(LOG_DEBUG,"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", chan->language, "");
+ 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, "|");
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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, 0777);
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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 */
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_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 */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
+ res = 't';
+ outmsg = 2;
+ break;
+ }
+ if (strchr(acceptdtmf, f->subclass)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
+ res = f->subclass;
+ outmsg = 2;
+ break;
+ }
+ if (strchr(canceldtmf, f->subclass)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
+ res = 't';
+ outmsg = 2;
+ break;
+ }
+ }
+ ast_frfree(f);
+ }
+ if (!f) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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 = others[0] ? ast_tellstream(others[0]) / 8000 : 0;
+
+ 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]);
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_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", chan->language, "");
+ }
+ 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_LIST_LOCK(&groups);
+ AST_LIST_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_LIST_REMOVE_CURRENT(&groups, list);
+ free(gi);
+ break;
+ }
+ }
+ AST_LIST_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_LIST_INSERT_TAIL(&groups, gi, list);
+ } else {
+ res = -1;
+ }
+
+ AST_LIST_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_LIST_LOCK(&groups);
+ AST_LIST_TRAVERSE(&groups, gi, list) {
+ if (!strcasecmp(gi->group, group) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category))))
+ count++;
+ }
+ AST_LIST_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_LIST_LOCK(&groups);
+ AST_LIST_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_LIST_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_LIST_LOCK(&groups);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&groups, gi, list) {
+ if (gi->chan == old) {
+ gi->chan = new;
+ } else if (gi->chan == new) {
+ AST_LIST_REMOVE_CURRENT(&groups, list);
+ free(gi);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&groups);
+
+ return 0;
+}
+
+int ast_app_group_discard(struct ast_channel *chan)
+{
+ struct ast_group_info *gi = NULL;
+
+ AST_LIST_LOCK(&groups);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&groups, gi, list) {
+ if (gi->chan == chan) {
+ AST_LIST_REMOVE_CURRENT(&groups, list);
+ free(gi);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&groups);
+
+ return 0;
+}
+
+int ast_app_group_list_lock(void)
+{
+ return AST_LIST_LOCK(&groups);
+}
+
+struct ast_group_info *ast_app_group_list_head(void)
+{
+ return AST_LIST_FIRST(&groups);
+}
+
+int ast_app_group_list_unlock(void)
+{
+ return AST_LIST_UNLOCK(&groups);
+}
+
+unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
+{
+ int argc;
+ char *scan, *wasdelim = NULL;
+ 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) {
+ wasdelim = scan;
+ *scan++ = '\0';
+ break;
+ }
+ }
+ }
+
+ /* If the last character in the original string was the delimiter, then
+ * there is one additional argument. */
+ if (*scan || (scan > buf && (scan - 1) == wasdelim)) {
+ array[argc++] = scan;
+ }
+
+ return argc;
+}
+
+enum AST_LOCK_RESULT ast_lock_path(const char *path)
+{
+ char *s;
+ char *fs;
+ int res;
+ int fd;
+ int lp = strlen(path);
+ time_t start;
+
+ if (!(s = alloca(lp + 10)) || !(fs = alloca(lp + 20))) {
+ ast_log(LOG_WARNING, "Out of memory!\n");
+ return AST_LOCK_FAILURE;
+ }
+
+ snprintf(fs, strlen(path) + 19, "%s/.lock-%08lx", path, ast_random());
+ fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ 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 {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Locked path '%s'\n", path);
+ return AST_LOCK_SUCCESS;
+ }
+}
+
+int ast_unlock_path(const char *path)
+{
+ char *s;
+ int res;
+
+ if (!(s = alloca(strlen(path) + 10))) {
+ ast_log(LOG_WARNING, "Out of memory!\n");
+ return -1;
+ }
+
+ 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 {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
+ }
+
+ return res;
+}
+
+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 == NULL) {
+ 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", chan->language, "");
+ cmd = 't';
+ return res;
+ }
+ case '2':
+ /* Review */
+ ast_verbose(VERBOSE_PREFIX_3 "Reviewing the recording\n");
+ cmd = ast_stream_and_wait(chan, recordfile, chan->language, AST_DIGIT_ANY);
+ break;
+ case '3':
+ message_exists = 0;
+ /* Record */
+ if (recorded == 1)
+ ast_verbose(VERBOSE_PREFIX_3 "Re-recording\n");
+ else
+ ast_verbose(VERBOSE_PREFIX_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, chan->language, 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, chan->language, "");
+ 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, treaat ast though ti 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, chan->language,
+ (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);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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")) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Invalid extension entered, going to 'i'!\n");
+ strcpy(exten, "i");
+ pos = 0;
+ continue;
+ } else {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Aborting on invalid entry, with no 'i' option!\n");
+ res = -2;
+ break;
+ }
+ } else {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "New existing extension: %s\n", exten);
+ pos = 0;
+ continue;
+ }
+ }
+ }
+ pos++;
+ }
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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;
+ char *output = NULL;
+ struct stat filesize;
+ int count = 0;
+ int res;
+ if (stat(filename, &filesize) == -1) {
+ ast_log(LOG_WARNING, "Error can't stat %s\n", filename);
+ return NULL;
+ }
+ count = filesize.st_size + 1;
+ fd = open(filename, O_RDONLY);
+ if (fd < 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));
+ free(output);
+ output = NULL;
+ }
+ }
+ close(fd);
+ return output;
+}
+
+void ast_app_options2str(const struct ast_app_option *options, struct ast_flags *flags, char *buf, size_t len)
+{
+ unsigned int i, found = 0;
+
+ for (i = 32; i < 128 && found < len;i++) {
+ if (ast_test_flag(flags, options[i].flag)) {
+ buf[found++] = i;
+ }
+ }
+ buf[found] = '\0';
+}
+
+int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
+{
+ char *s;
+ int curarg;
+ unsigned int argloc;
+ char *arg;
+ int res = 0;
+
+ 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;
+}
+
diff --git a/main/ast_expr2.c b/main/ast_expr2.c
new file mode 100644
index 000000000..66a680b56
--- /dev/null
+++ b/main/ast_expr2.c
@@ -0,0 +1,2859 @@
+/* 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_COLONCOLON = 258,
+ TOK_COND = 259,
+ TOK_OR = 260,
+ TOK_AND = 261,
+ TOK_NE = 262,
+ TOK_LE = 263,
+ TOK_GE = 264,
+ TOK_LT = 265,
+ TOK_GT = 266,
+ TOK_EQ = 267,
+ TOK_MINUS = 268,
+ TOK_PLUS = 269,
+ TOK_MOD = 270,
+ TOK_DIV = 271,
+ TOK_MULT = 272,
+ TOK_COMPL = 273,
+ TOK_EQTILDE = 274,
+ TOK_COLON = 275,
+ TOK_LP = 276,
+ TOK_RP = 277,
+ TOKEN = 278
+ };
+#endif
+/* Tokens. */
+#define TOK_COLONCOLON 258
+#define TOK_COND 259
+#define TOK_OR 260
+#define TOK_AND 261
+#define TOK_NE 262
+#define TOK_LE 263
+#define TOK_GE 264
+#define TOK_LT 265
+#define TOK_GT 266
+#define TOK_EQ 267
+#define TOK_MINUS 268
+#define TOK_PLUS 269
+#define TOK_MOD 270
+#define TOK_DIV 271
+#define TOK_MULT 272
+#define TOK_COMPL 273
+#define TOK_EQTILDE 274
+#define TOK_COLON 275
+#define TOK_LP 276
+#define TOK_RP 277
+#define TOKEN 278
+
+
+
+
+/* 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 "asterisk.h"
+
+#if !defined(STANDALONE_AEL)
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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"
+
+#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 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_integer, 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;
+ quad_t i;
+ } u;
+} ;
+
+typedef void *yyscan_t;
+
+struct parse_io
+{
+ char *string;
+ struct val *val;
+ yyscan_t scanner;
+};
+
+static int chk_div __P((quad_t, quad_t));
+static int chk_minus __P((quad_t, quad_t, quad_t));
+static int chk_plus __P((quad_t, quad_t, quad_t));
+static int chk_times __P((quad_t, quad_t, quad_t));
+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_integer __P((quad_t));
+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 quad_t to_integer __P((struct val *));
+static void to_string __P((struct val *));
+
+/* 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 147 "ast_expr2.y"
+{
+ struct val *val;
+}
+/* Line 198 of yacc.c. */
+#line 283 "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 151 "ast_expr2.y"
+
+extern int ast_yylex __P((YYSTYPE *, YYLTYPE *, yyscan_t));
+
+
+/* Line 221 of yacc.c. */
+#line 311 "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 10
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 140
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 24
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 3
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 23
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 46
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 278
+
+#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
+};
+
+#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, 16, 20, 24,
+ 28, 32, 36, 40, 44, 48, 52, 55, 58, 62,
+ 66, 70, 74, 78
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 25, 0, -1, 26, -1, -1, 23, -1, 21, 26,
+ 22, -1, 26, 5, 26, -1, 26, 6, 26, -1,
+ 26, 12, 26, -1, 26, 11, 26, -1, 26, 10,
+ 26, -1, 26, 9, 26, -1, 26, 8, 26, -1,
+ 26, 7, 26, -1, 26, 14, 26, -1, 26, 13,
+ 26, -1, 13, 26, -1, 18, 26, -1, 26, 17,
+ 26, -1, 26, 16, 26, -1, 26, 15, 26, -1,
+ 26, 20, 26, -1, 26, 19, 26, -1, 26, 4,
+ 26, 3, 26, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 175, 175, 183, 190, 191, 195, 199, 203, 207,
+ 211, 215, 219, 223, 227, 231, 235, 239, 243, 247,
+ 251, 255, 259, 263
+};
+#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_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", "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
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 24, 25, 25, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 0, 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, 4, 0, 2, 16, 17, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 5, 0, 6,
+ 7, 13, 12, 11, 10, 9, 8, 15, 14, 20,
+ 19, 18, 22, 21, 0, 23
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 5, 6
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -13
+static const yytype_int8 yypact[] =
+{
+ 109, 109, 109, 109, -13, 6, 59, 106, 106, 22,
+ -13, 109, 109, 109, 109, 109, 109, 109, 109, 109,
+ 109, 109, 109, 109, 109, 109, 109, -13, 42, 90,
+ 104, 120, 120, 120, 120, 120, 120, -12, -12, 106,
+ 106, 106, -13, -13, 109, 75
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -13, -13, -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, 22, 23, 24, 10, 25, 26, 0,
+ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 0, 25, 26, 45, 27, 44, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 0, 25, 26, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 0, 25, 26,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 0, 25, 26, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 0, 25,
+ 26, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 1, 25, 26, 25, 26, 2, 0, 0,
+ 3, 0, 4, 20, 21, 22, 23, 24, 0, 25,
+ 26
+};
+
+static const yytype_int8 yycheck[] =
+{
+ 1, 2, 3, 15, 16, 17, 0, 19, 20, -1,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ -1, 19, 20, 44, 22, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ -1, 19, 20, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, -1, 19, 20,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, -1, 19, 20, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, -1, 19,
+ 20, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 13, 19, 20, 19, 20, 18, -1, -1,
+ 21, -1, 23, 13, 14, 15, 16, 17, -1, 19,
+ 20
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 13, 18, 21, 23, 25, 26, 26, 26, 26,
+ 0, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 19, 20, 22, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 3, 26
+};
+
+#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 3: /* "TOK_COLONCOLON" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1257 "ast_expr2.c"
+ break;
+ case 4: /* "TOK_COND" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1262 "ast_expr2.c"
+ break;
+ case 5: /* "TOK_OR" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1267 "ast_expr2.c"
+ break;
+ case 6: /* "TOK_AND" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1272 "ast_expr2.c"
+ break;
+ case 7: /* "TOK_NE" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1277 "ast_expr2.c"
+ break;
+ case 8: /* "TOK_LE" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1282 "ast_expr2.c"
+ break;
+ case 9: /* "TOK_GE" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1287 "ast_expr2.c"
+ break;
+ case 10: /* "TOK_LT" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1292 "ast_expr2.c"
+ break;
+ case 11: /* "TOK_GT" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1297 "ast_expr2.c"
+ break;
+ case 12: /* "TOK_EQ" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1302 "ast_expr2.c"
+ break;
+ case 13: /* "TOK_MINUS" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1307 "ast_expr2.c"
+ break;
+ case 14: /* "TOK_PLUS" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1312 "ast_expr2.c"
+ break;
+ case 15: /* "TOK_MOD" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1317 "ast_expr2.c"
+ break;
+ case 16: /* "TOK_DIV" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1322 "ast_expr2.c"
+ break;
+ case 17: /* "TOK_MULT" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1327 "ast_expr2.c"
+ break;
+ case 18: /* "TOK_COMPL" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1332 "ast_expr2.c"
+ break;
+ case 19: /* "TOK_EQTILDE" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1337 "ast_expr2.c"
+ break;
+ case 20: /* "TOK_COLON" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1342 "ast_expr2.c"
+ break;
+ case 21: /* "TOK_LP" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1347 "ast_expr2.c"
+ break;
+ case 22: /* "TOK_RP" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1352 "ast_expr2.c"
+ break;
+ case 23: /* "TOKEN" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1357 "ast_expr2.c"
+ break;
+ case 26: /* "expr" */
+#line 169 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1362 "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 175 "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_integer )
+ ((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 183 "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 190 "ast_expr2.y"
+ { (yyval.val)= (yyvsp[(1) - (1)].val);;}
+ break;
+
+ case 5:
+#line 191 "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 6:
+#line 195 "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 7:
+#line 199 "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 8:
+#line 203 "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 9:
+#line 207 "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 10:
+#line 211 "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 11:
+#line 215 "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 12:
+#line 219 "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 13:
+#line 223 "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 14:
+#line 227 "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 15:
+#line 231 "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 16:
+#line 235 "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 17:
+#line 239 "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 18:
+#line 243 "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 19:
+#line 247 "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 20:
+#line 251 "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 21:
+#line 255 "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 22:
+#line 259 "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 23:
+#line 263 "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 1864 "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 270 "ast_expr2.y"
+
+
+static struct val *
+make_integer (quad_t 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_integer;
+ vp->u.i = i;
+ return vp;
+}
+
+static struct val *
+make_str (const char *s)
+{
+ struct val *vp;
+ size_t i;
+ int isint;
+
+ 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 = 1, isint = isdigit(s[0]) || s[0] == '-';
+ isint && i < strlen(s);
+ i++)
+ {
+ if(!isdigit(s[i]))
+ isint = 0;
+ }
+
+ 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 quad_t
+to_integer (struct val *vp)
+{
+ quad_t i;
+
+ if (vp == NULL) {
+ ast_log(LOG_WARNING,"vp==NULL in to_integer()\n");
+ return(0);
+ }
+
+ if (vp->type == AST_EXPR_integer)
+ return 1;
+
+ if (vp->type == AST_EXPR_string)
+ return 0;
+
+ /* vp->type == AST_EXPR_numeric_string, make it numeric */
+ errno = 0;
+ i = strtoll(vp->u.s, (char**)NULL, 10);
+ if (errno != 0) {
+ ast_log(LOG_WARNING,"Conversion of %s to integer 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_integer;
+ 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, "%ld", (long int) 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 integer */
+ return (vp->type == AST_EXPR_string);
+}
+
+
+static int
+is_zero_or_null (struct val *vp)
+{
+ if (vp->type == AST_EXPR_integer) {
+ return (vp->u.i == 0);
+ } else {
+ return (*vp->u.s == 0 || (to_integer (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));
+ printf("Expression: %s Result: [%d] '%s'\n",
+ s, ret, out);
+ }
+ fclose(infile);
+ }
+ else
+ {
+ if (ast_expr(argv[1], s, sizeof(s)))
+ 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 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_integer ((quad_t)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_integer ((quad_t)(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_integer(a);
+ (void)to_integer(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_integer ((quad_t)(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_integer ((quad_t)(strcoll (a->u.s, b->u.s) > 0));
+ } else {
+ (void)to_integer(a);
+ (void)to_integer(b);
+ r = make_integer ((quad_t)(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_integer ((quad_t)(strcoll (a->u.s, b->u.s) < 0));
+ } else {
+ (void)to_integer(a);
+ (void)to_integer(b);
+ r = make_integer ((quad_t)(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_integer ((quad_t)(strcoll (a->u.s, b->u.s) >= 0));
+ } else {
+ (void)to_integer(a);
+ (void)to_integer(b);
+ r = make_integer ((quad_t)(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_integer ((quad_t)(strcoll (a->u.s, b->u.s) <= 0));
+ } else {
+ (void)to_integer(a);
+ (void)to_integer(b);
+ r = make_integer ((quad_t)(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_integer(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_integer ((quad_t)(strcoll (a->u.s, b->u.s) != 0));
+ } else {
+ (void)to_integer(a);
+ (void)to_integer(b);
+ r = make_integer ((quad_t)(a->u.i != b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static int
+chk_plus (quad_t a, quad_t b, quad_t 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_integer (a)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING,"non-numeric argument\n");
+ if (!to_integer (b)) {
+ free_value(a);
+ free_value(b);
+ return make_integer(0);
+ } else {
+ free_value(a);
+ return (b);
+ }
+ } else if (!to_integer(b)) {
+ free_value(b);
+ return (a);
+ }
+
+ r = make_integer (/*(quad_t)*/(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 (quad_t a, quad_t b, quad_t 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_integer (a)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ if (!to_integer (b)) {
+ free_value(a);
+ free_value(b);
+ return make_integer(0);
+ } else {
+ r = make_integer(0 - b->u.i);
+ free_value(a);
+ free_value(b);
+ return (r);
+ }
+ } else if (!to_integer(b)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ free_value(b);
+ return (a);
+ }
+
+ r = make_integer (/*(quad_t)*/(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_integer (a) ) {
+ free_value(a);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_integer(0);
+ }
+
+ r = make_integer (/*(quad_t)*/(- 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_integer:
+ 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_integer (!v1);
+ free_value (a);
+ return r;
+}
+
+static int
+chk_times (quad_t a, quad_t b, quad_t 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_integer (a) || !to_integer (b)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return(make_integer(0));
+ }
+
+ r = make_integer (/*(quad_t)*/(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 (quad_t a, quad_t 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_integer (a)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_integer(0);
+ } else if (!to_integer (b)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_integer(INT_MAX);
+ }
+
+ if (b->u.i == 0) {
+ ast_log(LOG_WARNING, "division by zero\n");
+ free_value(a);
+ free_value(b);
+ return make_integer(INT_MAX);
+ }
+
+ r = make_integer (/*(quad_t)*/(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_integer (a) || !to_integer (b)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ free_value(a);
+ free_value(b);
+ return make_integer(0);
+ }
+
+ if (b->u.i == 0) {
+ ast_log(LOG_WARNING, "div by zero\n");
+ free_value(a);
+ return(b);
+ }
+
+ r = make_integer (/*(quad_t)*/(a->u.i % b->u.i));
+ /* 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_integer ((quad_t)(rm[0].rm_eo - rm[0].rm_so));
+ }
+ } else {
+ if (rp.re_nsub == 0) {
+ v = make_integer ((quad_t)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_integer ((quad_t)(rm[0].rm_eo - rm[0].rm_so));
+ }
+ } else {
+ if (rp.re_nsub == 0) {
+ v = make_integer ((quad_t)0);
+ } else {
+ v = make_str ("");
+ }
+ }
+
+ /* free arguments and pattern buffer */
+ free_value (a);
+ free_value (b);
+ regfree (&rp);
+
+ return v;
+}
+
diff --git a/main/ast_expr2.fl b/main/ast_expr2.fl
new file mode 100644
index 000000000..67dd3aa6a
--- /dev/null
+++ b/main/ast_expr2.fl
@@ -0,0 +1,414 @@
+%{
+/*
+ * 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"
+
+#if !defined(STANDALONE_AEL)
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#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"
+#include "asterisk/strings.h"
+
+enum valtype {
+ AST_EXPR_integer, AST_EXPR_numeric_string, AST_EXPR_string
+} ;
+
+struct val {
+ enum valtype type;
+ union {
+ char *s;
+ quad_t i;
+ } 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;
+};
+
+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 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_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]+ {
+ 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,.';\\_^$#@]+ {
+ 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 parse_io io;
+ int return_value = 0;
+
+ memset(&io, 0, sizeof(io));
+ io.string = expr; /* to pass to the error routine */
+
+ 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_integer) {
+ int res_length;
+
+ res_length = snprintf(buf, length, "%ld", (long int) io.val->u.i);
+ return_value = (res_length <= length) ? res_length : length;
+ } else {
+#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 */
+ return_value = strlen(buf);
+ 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_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/main/ast_expr2.h b/main/ast_expr2.h
new file mode 100644
index 000000000..d4490512e
--- /dev/null
+++ b/main/ast_expr2.h
@@ -0,0 +1,112 @@
+/* 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_COLONCOLON = 258,
+ TOK_COND = 259,
+ TOK_OR = 260,
+ TOK_AND = 261,
+ TOK_NE = 262,
+ TOK_LE = 263,
+ TOK_GE = 264,
+ TOK_LT = 265,
+ TOK_GT = 266,
+ TOK_EQ = 267,
+ TOK_MINUS = 268,
+ TOK_PLUS = 269,
+ TOK_MOD = 270,
+ TOK_DIV = 271,
+ TOK_MULT = 272,
+ TOK_COMPL = 273,
+ TOK_EQTILDE = 274,
+ TOK_COLON = 275,
+ TOK_LP = 276,
+ TOK_RP = 277,
+ TOKEN = 278
+ };
+#endif
+/* Tokens. */
+#define TOK_COLONCOLON 258
+#define TOK_COND 259
+#define TOK_OR 260
+#define TOK_AND 261
+#define TOK_NE 262
+#define TOK_LE 263
+#define TOK_GE 264
+#define TOK_LT 265
+#define TOK_GT 266
+#define TOK_EQ 267
+#define TOK_MINUS 268
+#define TOK_PLUS 269
+#define TOK_MOD 270
+#define TOK_DIV 271
+#define TOK_MULT 272
+#define TOK_COMPL 273
+#define TOK_EQTILDE 274
+#define TOK_COLON 275
+#define TOK_LP 276
+#define TOK_RP 277
+#define TOKEN 278
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 147 "ast_expr2.y"
+{
+ struct val *val;
+}
+/* Line 1536 of yacc.c. */
+#line 89 "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/main/ast_expr2.y b/main/ast_expr2.y
new file mode 100644
index 000000000..b2faf374d
--- /dev/null
+++ b/main/ast_expr2.y
@@ -0,0 +1,1045 @@
+%{
+/* 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 "asterisk.h"
+
+#if !defined(STANDALONE_AEL)
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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"
+
+#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 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_integer, 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;
+ quad_t i;
+ } u;
+} ;
+
+typedef void *yyscan_t;
+
+struct parse_io
+{
+ char *string;
+ struct val *val;
+ yyscan_t scanner;
+};
+
+static int chk_div __P((quad_t, quad_t));
+static int chk_minus __P((quad_t, quad_t, quad_t));
+static int chk_plus __P((quad_t, quad_t, quad_t));
+static int chk_times __P((quad_t, quad_t, quad_t));
+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_integer __P((quad_t));
+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 quad_t to_integer __P((struct val *));
+static void to_string __P((struct val *));
+
+/* 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;
+}
+
+%{
+extern int ast_yylex __P((YYSTYPE *, YYLTYPE *, yyscan_t));
+%}
+%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 <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_integer )
+ ((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("");
+ }
+
+ ;
+
+expr: 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 val *
+make_integer (quad_t 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_integer;
+ vp->u.i = i;
+ return vp;
+}
+
+static struct val *
+make_str (const char *s)
+{
+ struct val *vp;
+ size_t i;
+ int isint;
+
+ 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 = 1, isint = isdigit(s[0]) || s[0] == '-';
+ isint && i < strlen(s);
+ i++)
+ {
+ if(!isdigit(s[i]))
+ isint = 0;
+ }
+
+ 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 quad_t
+to_integer (struct val *vp)
+{
+ quad_t i;
+
+ if (vp == NULL) {
+ ast_log(LOG_WARNING,"vp==NULL in to_integer()\n");
+ return(0);
+ }
+
+ if (vp->type == AST_EXPR_integer)
+ return 1;
+
+ if (vp->type == AST_EXPR_string)
+ return 0;
+
+ /* vp->type == AST_EXPR_numeric_string, make it numeric */
+ errno = 0;
+ i = strtoll(vp->u.s, (char**)NULL, 10);
+ if (errno != 0) {
+ ast_log(LOG_WARNING,"Conversion of %s to integer 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_integer;
+ 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, "%ld", (long int) 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 integer */
+ return (vp->type == AST_EXPR_string);
+}
+
+
+static int
+is_zero_or_null (struct val *vp)
+{
+ if (vp->type == AST_EXPR_integer) {
+ return (vp->u.i == 0);
+ } else {
+ return (*vp->u.s == 0 || (to_integer (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));
+ printf("Expression: %s Result: [%d] '%s'\n",
+ s, ret, out);
+ }
+ fclose(infile);
+ }
+ else
+ {
+ if (ast_expr(argv[1], s, sizeof(s)))
+ 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 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_integer ((quad_t)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_integer ((quad_t)(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_integer(a);
+ (void)to_integer(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_integer ((quad_t)(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_integer ((quad_t)(strcoll (a->u.s, b->u.s) > 0));
+ } else {
+ (void)to_integer(a);
+ (void)to_integer(b);
+ r = make_integer ((quad_t)(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_integer ((quad_t)(strcoll (a->u.s, b->u.s) < 0));
+ } else {
+ (void)to_integer(a);
+ (void)to_integer(b);
+ r = make_integer ((quad_t)(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_integer ((quad_t)(strcoll (a->u.s, b->u.s) >= 0));
+ } else {
+ (void)to_integer(a);
+ (void)to_integer(b);
+ r = make_integer ((quad_t)(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_integer ((quad_t)(strcoll (a->u.s, b->u.s) <= 0));
+ } else {
+ (void)to_integer(a);
+ (void)to_integer(b);
+ r = make_integer ((quad_t)(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_integer(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_integer ((quad_t)(strcoll (a->u.s, b->u.s) != 0));
+ } else {
+ (void)to_integer(a);
+ (void)to_integer(b);
+ r = make_integer ((quad_t)(a->u.i != b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static int
+chk_plus (quad_t a, quad_t b, quad_t 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_integer (a)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING,"non-numeric argument\n");
+ if (!to_integer (b)) {
+ free_value(a);
+ free_value(b);
+ return make_integer(0);
+ } else {
+ free_value(a);
+ return (b);
+ }
+ } else if (!to_integer(b)) {
+ free_value(b);
+ return (a);
+ }
+
+ r = make_integer (/*(quad_t)*/(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 (quad_t a, quad_t b, quad_t 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_integer (a)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ if (!to_integer (b)) {
+ free_value(a);
+ free_value(b);
+ return make_integer(0);
+ } else {
+ r = make_integer(0 - b->u.i);
+ free_value(a);
+ free_value(b);
+ return (r);
+ }
+ } else if (!to_integer(b)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ free_value(b);
+ return (a);
+ }
+
+ r = make_integer (/*(quad_t)*/(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_integer (a) ) {
+ free_value(a);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_integer(0);
+ }
+
+ r = make_integer (/*(quad_t)*/(- 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_integer:
+ 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_integer (!v1);
+ free_value (a);
+ return r;
+}
+
+static int
+chk_times (quad_t a, quad_t b, quad_t 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_integer (a) || !to_integer (b)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return(make_integer(0));
+ }
+
+ r = make_integer (/*(quad_t)*/(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 (quad_t a, quad_t 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_integer (a)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_integer(0);
+ } else if (!to_integer (b)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_integer(INT_MAX);
+ }
+
+ if (b->u.i == 0) {
+ ast_log(LOG_WARNING, "division by zero\n");
+ free_value(a);
+ free_value(b);
+ return make_integer(INT_MAX);
+ }
+
+ r = make_integer (/*(quad_t)*/(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_integer (a) || !to_integer (b)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ free_value(a);
+ free_value(b);
+ return make_integer(0);
+ }
+
+ if (b->u.i == 0) {
+ ast_log(LOG_WARNING, "div by zero\n");
+ free_value(a);
+ return(b);
+ }
+
+ r = make_integer (/*(quad_t)*/(a->u.i % b->u.i));
+ /* 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_integer ((quad_t)(rm[0].rm_eo - rm[0].rm_so));
+ }
+ } else {
+ if (rp.re_nsub == 0) {
+ v = make_integer ((quad_t)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_integer ((quad_t)(rm[0].rm_eo - rm[0].rm_so));
+ }
+ } else {
+ if (rp.re_nsub == 0) {
+ v = make_integer ((quad_t)0);
+ } else {
+ v = make_str ("");
+ }
+ }
+
+ /* free arguments and pattern buffer */
+ free_value (a);
+ free_value (b);
+ regfree (&rp);
+
+ return v;
+}
diff --git a/main/ast_expr2f.c b/main/ast_expr2f.c
new file mode 100644
index 000000000..8cc75cfd5
--- /dev/null
+++ b/main/ast_expr2f.c
@@ -0,0 +1,3349 @@
+#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 35
+#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;
+
+/* 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 /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__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
+
+/* 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 )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t 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 char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+static yyconst flex_int16_t yy_nxt[][128] =
+ {
+ {
+ 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, 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, 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, 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, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+ },
+
+ {
+ 7, 8, 8, 8, 8, 8, 8, 8, 8, 9,
+ 10, 8, 8, 9, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 9, 11, 12, 13, 14, 15, 16, 13,
+ 17, 18, 19, 20, 13, 21, 13, 22, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 24, 13,
+ 25, 26, 27, 28, 13, 13, 13, 13, 13, 13,
+
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 8, 13, 8, 13, 13, 8, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 8, 29, 8, 8, 8
+ },
+
+ {
+ 7, 8, 8, 8, 8, 8, 8, 8, 8, 9,
+ 10, 8, 8, 9, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 9, 11, 12, 13, 14, 15, 16, 13,
+
+ 17, 18, 19, 20, 13, 21, 13, 22, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 24, 13,
+ 25, 26, 27, 28, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 8, 13, 8, 13, 13, 8, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 8, 29, 8, 8, 8
+ },
+
+ {
+ 7, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 31, 30, 32, 30, 30
+ },
+
+ {
+ 7, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 31, 30, 32, 30, 30
+ },
+
+ {
+ 7, 33, 33, 33, 33, 33, 33, 33, 33, 34,
+ 34, 33, 33, 34, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 34, 34, 33, 33, 35, 34, 34, 33,
+ 34, 34, 34, 34, 33, 34, 33, 34, 33, 33,
+
+ 33, 33, 33, 33, 33, 33, 33, 33, 34, 33,
+ 34, 34, 34, 34, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 34, 33, 33, 33
+ },
+
+ {
+ 7, 33, 33, 33, 33, 33, 33, 33, 33, 34,
+ 34, 33, 33, 34, 33, 33, 33, 33, 33, 33,
+
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 34, 34, 33, 33, 35, 34, 34, 33,
+ 34, 34, 34, 34, 33, 34, 33, 34, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 34, 33,
+ 34, 34, 34, 34, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+
+ 33, 33, 33, 33, 34, 33, 33, 33
+ },
+
+ {
+ -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
+ },
+
+ {
+ 7, -8, -8, -8, -8, -8, -8, -8, -8, -8,
+ -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
+ -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
+ -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
+ -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
+ -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
+
+ -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
+ -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
+ -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
+ -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
+ -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
+ -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
+ -8, -8, -8, -8, -8, -8, -8, -8
+ },
+
+ {
+ 7, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9
+
+ },
+
+ {
+ 7, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10
+ },
+
+ {
+ 7, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, 36, -11, -11, -11, -11, -11, -11, -11, -11,
+
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11
+ },
+
+ {
+ 7, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 38, 37, 37, 37, 37, 37,
+
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37
+ },
+
+ {
+ 7, -13, -13, -13, -13, -13, -13, -13, -13, -13,
+
+ -13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
+ -13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
+ -13, -13, -13, -13, -13, 39, 39, -13, -13, 39,
+ -13, -13, -13, -13, 39, -13, 39, -13, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, -13, 39,
+ -13, -13, -13, -13, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, -13, 39, -13, 39, 39, -13, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, -13, -13, -13, -13, -13
+ },
+
+ {
+ 7, -14, -14, -14, -14, -14, -14, -14, -14, -14,
+ -14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
+ -14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
+ -14, -14, -14, -14, -14, 39, 39, -14, -14, 39,
+ -14, -14, -14, -14, 39, -14, 39, -14, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, -14, 39,
+ -14, -14, -14, -14, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, -14, 39, -14, 39, 39, -14, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 40, -14, -14, -14, -14
+ },
+
+ {
+ 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, -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, -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, -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, -15, -15, -15, -15, -15,
+ -15, -15, -15, -15, -15, -15, -15, -15, -15, -15,
+ -15, -15, -15, -15, -15, -15, -15, -15
+ },
+
+ {
+ 7, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+
+ -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, 41, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+
+ -16, -16, -16, -16, -16, -16, -16, -16
+ },
+
+ {
+ 7, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17
+ },
+
+ {
+ 7, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18
+ },
+
+ {
+ 7, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19
+
+ },
+
+ {
+ 7, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20
+ },
+
+ {
+ 7, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21
+ },
+
+ {
+ 7, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22
+ },
+
+ {
+ 7, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+
+ -23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, 39, 39, -23, -23, 39,
+ -23, -23, -23, -23, 39, -23, 39, -23, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, -23, 39,
+ -23, -23, -23, -23, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, -23, 39, -23, 39, 39, -23, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, -23, -23, -23, -23, -23
+ },
+
+ {
+ 7, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24, -24, 43, -24,
+ -24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+
+ -24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24, -24
+ },
+
+ {
+ 7, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, 44, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25
+ },
+
+ {
+ 7, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, 45, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+
+ -26, -26, -26, -26, -26, -26, 46, -26
+ },
+
+ {
+ 7, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, 47, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27
+ },
+
+ {
+ 7, -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
+ },
+
+ {
+ 7, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, 48, -29, -29, -29
+
+ },
+
+ {
+ 7, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 50, 49, 51, 49, 49
+ },
+
+ {
+ 7, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31
+ },
+
+ {
+ 7, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32
+ },
+
+ {
+ 7, 52, 52, 52, 52, 52, 52, 52, 52, -33,
+
+ -33, 52, 52, -33, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, -33, -33, 52, 52, -33, -33, -33, 52,
+ -33, -33, -33, -33, 52, -33, 52, -33, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, -33, 52,
+ -33, -33, -33, -33, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, -33, 52, 52, 52
+ },
+
+ {
+ 7, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34
+ },
+
+ {
+ 7, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, 53, -35, -35, -35, -35
+ },
+
+ {
+ 7, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+
+ -36, -36, -36, -36, -36, -36, -36, -36
+ },
+
+ {
+ 7, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 38, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37
+ },
+
+ {
+ 7, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38
+ },
+
+ {
+ 7, -39, -39, -39, -39, -39, -39, -39, -39, -39,
+ -39, -39, -39, -39, -39, -39, -39, -39, -39, -39,
+ -39, -39, -39, -39, -39, -39, -39, -39, -39, -39,
+
+ -39, -39, -39, -39, -39, 39, 39, -39, -39, 39,
+ -39, -39, -39, -39, 39, -39, 39, -39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, -39, 39,
+ -39, -39, -39, -39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, -39, 39, -39, 39, 39, -39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, -39, -39, -39, -39, -39
+
+ },
+
+ {
+ 7, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40
+ },
+
+ {
+ 7, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41
+ },
+
+ {
+ 7, -42, -42, -42, -42, -42, -42, -42, -42, -42,
+ -42, -42, -42, -42, -42, -42, -42, -42, -42, -42,
+ -42, -42, -42, -42, -42, -42, -42, -42, -42, -42,
+ -42, -42, -42, -42, -42, 39, 39, -42, -42, 39,
+
+ -42, -42, -42, -42, 39, -42, 39, -42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, -42, 39,
+ -42, -42, -42, -42, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, -42, 39, -42, 39, 39, -42, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, -42, -42, -42, -42, -42
+ },
+
+ {
+ 7, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+
+ -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+
+ -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43, -43
+ },
+
+ {
+ 7, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44
+ },
+
+ {
+ 7, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45
+ },
+
+ {
+ 7, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+
+ -46, -46, -46, -46, -46, -46, -46, -46
+ },
+
+ {
+ 7, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+ -47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+ -47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+ -47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+ -47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+ -47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+ -47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+ -47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+ -47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+
+ -47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+ -47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+ -47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+ -47, -47, -47, -47, -47, -47, -47, -47
+ },
+
+ {
+ 7, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48
+ },
+
+ {
+ 7, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 50, 49, 51, 49, 49
+
+ },
+
+ {
+ 7, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50
+ },
+
+ {
+ 7, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51
+ },
+
+ {
+ 7, 52, 52, 52, 52, 52, 52, 52, 52, -52,
+ -52, 52, 52, -52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, -52, -52, 52, 52, -52, -52, -52, 52,
+
+ -52, -52, -52, -52, 52, -52, 52, -52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, -52, 52,
+ -52, -52, -52, -52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, -52, 52, 52, 52
+ },
+
+ {
+ 7, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53
+ },
+
+ } ;
+
+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 35
+#define YY_END_OF_BUFFER 36
+/* 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[54] =
+ { 0,
+ 0, 0, 0, 0, 32, 32, 36, 35, 25, 27,
+ 19, 35, 29, 29, 17, 2, 22, 23, 15, 13,
+ 14, 16, 28, 20, 9, 3, 8, 18, 1, 35,
+ 31, 30, 32, 33, 33, 12, 0, 26, 29, 24,
+ 5, 28, 21, 11, 6, 7, 10, 4, 0, 31,
+ 30, 32, 34
+ } ;
+
+static yyconst yy_state_type yy_NUL_trans[54] =
+ { 0,
+ 8, 8, 30, 30, 33, 33, 0, 0, 0, 0,
+ 0, 37, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 49,
+ 0, 0, 52, 0, 0, 0, 37, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 49, 0,
+ 0, 52, 0
+ } ;
+
+/* 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"
+
+#if !defined(STANDALONE_AEL)
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#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"
+#include "asterisk/strings.h"
+
+enum valtype {
+ AST_EXPR_integer, AST_EXPR_numeric_string, AST_EXPR_string
+} ;
+
+struct val {
+ enum valtype type;
+ union {
+ char *s;
+ quad_t i;
+ } 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;
+};
+
+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 1422 "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
+
+int ast_yylex_init (yyscan_t* scanner);
+
+int ast_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* 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 do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#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) \
+ errno=0; \
+ while ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \
+ { \
+ 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 105 "ast_expr2.fl"
+
+
+#line 1652 "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:
+ while ( (yy_current_state = yy_nxt[yy_current_state][ YY_SC_TO_UI(*yy_cp) ]) > 0 )
+ {
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+
+ ++yy_cp;
+ }
+
+ yy_current_state = -yy_current_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 + 1;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 107 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_OR;}
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 108 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_AND;}
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 109 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_EQ;}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 110 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_OR;}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 111 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_AND;}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 112 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_EQ;}
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 113 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_EQTILDE;}
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 114 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_GT;}
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 115 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_LT;}
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 116 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_GE;}
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 117 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_LE;}
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 118 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_NE;}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 119 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_PLUS;}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 120 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_MINUS;}
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 121 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_MULT;}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 122 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_DIV;}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 123 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_MOD;}
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 124 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COND;}
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 125 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COMPL;}
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 126 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COLON;}
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 127 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COLONCOLON;}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 128 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_LP;}
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 129 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_RP;}
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 130 "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 25:
+YY_RULE_SETUP
+#line 140 "ast_expr2.fl"
+{}
+ YY_BREAK
+case 26:
+/* rule 26 can match eol */
+YY_RULE_SETUP
+#line 141 "ast_expr2.fl"
+{SET_COLUMNS; SET_STRING; return TOKEN;}
+ YY_BREAK
+case 27:
+/* rule 27 can match eol */
+YY_RULE_SETUP
+#line 143 "ast_expr2.fl"
+{/* what to do with eol */}
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 144 "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 29:
+YY_RULE_SETUP
+#line 153 "ast_expr2.fl"
+{
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+ YY_BREAK
+case 30:
+/* rule 30 can match eol */
+YY_RULE_SETUP
+#line 160 "ast_expr2.fl"
+{
+ curlycount--;
+ if (curlycount < 0) {
+ BEGIN(trail);
+ yymore();
+ } else {
+ yymore();
+ }
+ }
+ YY_BREAK
+case 31:
+/* rule 31 can match eol */
+YY_RULE_SETUP
+#line 170 "ast_expr2.fl"
+{
+ curlycount++;
+ yymore();
+ }
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 176 "ast_expr2.fl"
+{
+ BEGIN(0);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+ YY_BREAK
+case 33:
+/* rule 33 can match eol */
+YY_RULE_SETUP
+#line 183 "ast_expr2.fl"
+{
+ char c = yytext[yyleng-1];
+ BEGIN(0);
+ unput(c);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 192 "ast_expr2.fl"
+{
+ curlycount = 0;
+ BEGIN(var);
+ yymore();
+ }
+ YY_BREAK
+case YY_STATE_EOF(trail):
+#line 198 "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 35:
+YY_RULE_SETUP
+#line 206 "ast_expr2.fl"
+ECHO;
+ YY_BREAK
+#line 1969 "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_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 ( 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, (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;
+ 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;
+
+ if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) ast_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ 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 )
+ {
+ if ( *yy_cp )
+ {
+ yy_current_state = yy_nxt[yy_current_state][YY_SC_TO_UI(*yy_cp)];
+ }
+ else
+ yy_current_state = yy_NUL_trans[yy_current_state];
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ }
+
+ 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;
+
+ yy_current_state = yy_NUL_trans[yy_current_state];
+ yy_is_jam = (yy_current_state == 0);
+
+ if ( ! yy_is_jam )
+ {
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ }
+
+ 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);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in ast_yyensure_buffer_stack()" );
+
+ 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);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in ast_yyensure_buffer_stack()" );
+
+ /* 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 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
+ * 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 );
+}
+
+/* ast_yylex_init_extra has the same functionality as ast_yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to ast_yyalloc in
+ * the yyextra field.
+ */
+
+int ast_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+ struct yyguts_t dummy_yyguts;
+
+ ast_yyset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) ast_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+ 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));
+
+ ast_yyset_extra (yy_user_defined, *ptr_yy_globals);
+
+ 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 206 "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 parse_io io;
+ int return_value = 0;
+
+ memset(&io, 0, sizeof(io));
+ io.string = expr; /* to pass to the error routine */
+
+ 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_integer) {
+ int res_length;
+
+ res_length = snprintf(buf, length, "%ld", (long int) io.val->u.i);
+ return_value = (res_length <= length) ? res_length : length;
+ } else {
+#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 */
+ return_value = strlen(buf);
+ 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_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/main/asterisk.c b/main/asterisk.c
new file mode 100644
index 000000000..8ef99c683
--- /dev/null
+++ b/main/asterisk.c
@@ -0,0 +1,3223 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2008, 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 Licensing
+ * \arg \ref DevDoc
+ * \arg \ref ConfigFiles
+ *
+ * \section copyright Copyright and author
+ *
+ * Copyright (C) 1999 - 2008, Digium, Inc.
+ * Asterisk is a trademark registered by Digium, Inc.
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * Also see \ref AstCREDITS
+ *
+ * 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.
+ */
+
+/*! \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$")
+
+#undef sched_setscheduler
+#undef setpriority
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sched.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/resource.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/stat.h>
+
+#if defined(HAVE_ZAPTEL) || defined (HAVE_DAHDI)
+#include <sys/ioctl.h>
+#include "asterisk/dahdi_compat.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(__FreeBSD__) || defined( __NetBSD__ ) || defined(SOLARIS)
+#include <netdb.h>
+#if defined(SOLARIS)
+int daemon(int, int); /* defined in libresolv of all places */
+#endif
+#endif
+
+#include "asterisk/logger.h"
+#include "asterisk/options.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 "asterisk/lock.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 " ASTERISK_VERSION ", Copyright (C) 1999 - 2008 Digium, Inc. and others.\n"); \
+ ast_verbose("Created by Mark Spencer <markster@digium.com>\n"); \
+ ast_verbose("Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details.\n"); \
+ ast_verbose("This is free software, with components licensed under the GNU General Public\n"); \
+ ast_verbose("License version 2 and other licenses; you are welcome to redistribute it under\n"); \
+ ast_verbose("certain conditions. Type 'core show license' for details.\n"); \
+ ast_verbose("=========================================================================\n")
+
+/*! \defgroup main_options Main Configuration Options
+ \brief Main configuration options from \ref Config_ast "asterisk.conf" or
+ the operating system command line when starting Asterisk
+ 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 */
+
+/*! @} */
+
+char record_cache_dir[AST_CACHE_DIR_LEN] = AST_TMP_DIR;
+char debug_filename[AST_FILENAME_MAX] = "";
+#ifdef HAVE_ZAPTEL
+static char _dahdi_chan_name[AST_CHANNEL_NAME] = "Zap";
+static size_t _dahdi_chan_name_len = 3;
+static enum dahdi_chan_modes _dahdi_chan_mode = CHAN_ZAP_MODE;
+#else
+static char _dahdi_chan_name[AST_CHANNEL_NAME] = "DAHDI";
+static size_t _dahdi_chan_name_len = 5;
+static enum dahdi_chan_modes _dahdi_chan_mode = CHAN_DAHDI_PLUS_ZAP_MODE;
+#endif
+const char *dahdi_chan_name;
+const size_t *dahdi_chan_name_len;
+const enum dahdi_chan_modes *dahdi_chan_mode;
+
+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_LIST_ENTRY(ast_atexit) list;
+};
+
+static AST_LIST_HEAD_STATIC(atexits, ast_atexit);
+
+time_t ast_startuptime;
+time_t 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 *);
+
+char ast_config_AST_CONFIG_DIR[PATH_MAX];
+char ast_config_AST_CONFIG_FILE[PATH_MAX];
+char ast_config_AST_MODULE_DIR[PATH_MAX];
+char ast_config_AST_SPOOL_DIR[PATH_MAX];
+char ast_config_AST_MONITOR_DIR[PATH_MAX];
+char ast_config_AST_VAR_DIR[PATH_MAX];
+char ast_config_AST_DATA_DIR[PATH_MAX];
+char ast_config_AST_LOG_DIR[PATH_MAX];
+char ast_config_AST_AGI_DIR[PATH_MAX];
+char ast_config_AST_DB[PATH_MAX];
+char ast_config_AST_KEY_DIR[PATH_MAX];
+char ast_config_AST_PID[PATH_MAX];
+char ast_config_AST_SOCKET[PATH_MAX];
+char ast_config_AST_RUN_DIR[PATH_MAX];
+char ast_config_AST_RUN_USER[PATH_MAX];
+char ast_config_AST_RUN_GROUP[PATH_MAX];
+char ast_config_AST_CTL_PERMISSIONS[PATH_MAX];
+char ast_config_AST_CTL_OWNER[PATH_MAX] = "\0";
+char ast_config_AST_CTL_GROUP[PATH_MAX] = "\0";
+char ast_config_AST_CTL[PATH_MAX] = "asterisk.ctl";
+char ast_config_AST_SYSTEM_NAME[20] = "";
+
+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 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_LIST_ENTRY(file_version) list;
+ const char *file;
+ char *version;
+};
+
+static AST_LIST_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_LIST_LOCK(&file_versions);
+ AST_LIST_INSERT_HEAD(&file_versions, new, list);
+ AST_LIST_UNLOCK(&file_versions);
+}
+
+void ast_unregister_file_version(const char *file)
+{
+ struct file_version *find;
+
+ AST_LIST_LOCK(&file_versions);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&file_versions, find, list) {
+ if (!strcasecmp(find->file, file)) {
+ AST_LIST_REMOVE_CURRENT(&file_versions, list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&file_versions);
+ if (find)
+ free(find);
+}
+
+struct thread_list_t {
+ AST_LIST_ENTRY(thread_list_t) list;
+ char *name;
+ pthread_t id;
+};
+
+static AST_LIST_HEAD_STATIC(thread_list, thread_list_t);
+
+static char show_threads_help[] =
+"Usage: core show threads\n"
+" List threads currently active in the system.\n";
+
+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_LIST_LOCK(&thread_list);
+ AST_LIST_INSERT_HEAD(&thread_list, new, list);
+ AST_LIST_UNLOCK(&thread_list);
+}
+
+void ast_unregister_thread(void *id)
+{
+ struct thread_list_t *x;
+
+ AST_LIST_LOCK(&thread_list);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&thread_list, x, list) {
+ if ((void *) x->id == id) {
+ AST_LIST_REMOVE_CURRENT(&thread_list, list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&thread_list);
+ if (x) {
+ free(x->name);
+ free(x);
+ }
+}
+
+static int handle_show_threads(int fd, int argc, char *argv[])
+{
+ int count = 0;
+ struct thread_list_t *cur;
+
+ AST_LIST_LOCK(&thread_list);
+ AST_LIST_TRAVERSE(&thread_list, cur, list) {
+ ast_cli(fd, "%p %s\n", (void *)cur->id, cur->name);
+ count++;
+ }
+ AST_LIST_UNLOCK(&thread_list);
+ ast_cli(fd, "%d threads listed.\n", count);
+ return 0;
+}
+
+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;
+}
+
+/* The RDTSC instruction was introduced on the Pentium processor and is not
+ * implemented on certain clones, like the Cyrix 586. Hence, the previous
+ * expectation of __i386__ was in error. */
+#if defined ( __i686__) && (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;
+}
+
+static int handle_show_profile_deprecated(int fd, int argc, char *argv[])
+{
+ int i, min, max;
+ char *search = NULL;
+
+ if (prof_data == NULL)
+ return 0;
+
+ min = 0;
+ max = prof_data->entries;
+ if (argc >= 3) { /* specific entries */
+ if (isdigit(argv[2][0])) {
+ min = atoi(argv[2]);
+ if (argc == 4 && strcmp(argv[3], "-"))
+ max = atoi(argv[3]);
+ } else
+ search = argv[2];
+ }
+ if (max > prof_data->entries)
+ max = prof_data->entries;
+ if (!strcmp(argv[0], "clear")) {
+ 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 0;
+ }
+ ast_cli(fd, "profile values (%d, allocated %d)\n-------------------\n",
+ prof_data->entries, prof_data->max_size);
+ ast_cli(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(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 0;
+}
+
+static int handle_show_profile(int fd, int argc, char *argv[])
+{
+ int i, min, max;
+ char *search = NULL;
+
+ if (prof_data == NULL)
+ return 0;
+
+ min = 0;
+ max = prof_data->entries;
+ if (argc > 3) { /* specific entries */
+ if (isdigit(argv[3][0])) {
+ min = atoi(argv[3]);
+ if (argc == 5 && strcmp(argv[4], "-"))
+ max = atoi(argv[4]);
+ } else
+ search = argv[3];
+ }
+ if (max > prof_data->entries)
+ max = prof_data->entries;
+ if (!strcmp(argv[1], "clear")) {
+ 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 0;
+ }
+ ast_cli(fd, "profile values (%d, allocated %d)\n-------------------\n",
+ prof_data->entries, prof_data->max_size);
+ ast_cli(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(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 0;
+}
+
+static char show_version_files_help[] =
+"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";
+
+/*! \brief CLI command to list module versions */
+static int handle_show_version_files_deprecated(int fd, int argc, char *argv[])
+{
+#define FORMAT "%-25.25s %-40.40s\n"
+ struct file_version *iterator;
+ regex_t regexbuf;
+ int havepattern = 0;
+ int havename = 0;
+ int count_files = 0;
+
+ switch (argc) {
+ case 5:
+ if (!strcasecmp(argv[3], "like")) {
+ if (regcomp(&regexbuf, argv[4], REG_EXTENDED | REG_NOSUB))
+ return RESULT_SHOWUSAGE;
+ havepattern = 1;
+ } else
+ return RESULT_SHOWUSAGE;
+ break;
+ case 4:
+ havename = 1;
+ break;
+ case 3:
+ break;
+ default:
+ return RESULT_SHOWUSAGE;
+ }
+
+ ast_cli(fd, FORMAT, "File", "Revision");
+ ast_cli(fd, FORMAT, "----", "--------");
+ AST_LIST_LOCK(&file_versions);
+ AST_LIST_TRAVERSE(&file_versions, iterator, list) {
+ if (havename && strcasecmp(iterator->file, argv[3]))
+ continue;
+
+ if (havepattern && regexec(&regexbuf, iterator->file, 0, NULL, 0))
+ continue;
+
+ ast_cli(fd, FORMAT, iterator->file, iterator->version);
+ count_files++;
+ if (havename)
+ break;
+ }
+ AST_LIST_UNLOCK(&file_versions);
+ if (!havename) {
+ ast_cli(fd, "%d files listed.\n", count_files);
+ }
+
+ if (havepattern)
+ regfree(&regexbuf);
+
+ return RESULT_SUCCESS;
+#undef FORMAT
+}
+
+static int handle_show_version_files(int fd, int argc, char *argv[])
+{
+#define FORMAT "%-25.25s %-40.40s\n"
+ struct file_version *iterator;
+ regex_t regexbuf;
+ int havepattern = 0;
+ int havename = 0;
+ int count_files = 0;
+
+ switch (argc) {
+ case 6:
+ if (!strcasecmp(argv[4], "like")) {
+ if (regcomp(&regexbuf, argv[5], REG_EXTENDED | REG_NOSUB))
+ return RESULT_SHOWUSAGE;
+ havepattern = 1;
+ } else
+ return RESULT_SHOWUSAGE;
+ break;
+ case 5:
+ havename = 1;
+ break;
+ case 4:
+ break;
+ default:
+ return RESULT_SHOWUSAGE;
+ }
+
+ ast_cli(fd, FORMAT, "File", "Revision");
+ ast_cli(fd, FORMAT, "----", "--------");
+ AST_LIST_LOCK(&file_versions);
+ AST_LIST_TRAVERSE(&file_versions, iterator, list) {
+ if (havename && strcasecmp(iterator->file, argv[4]))
+ continue;
+
+ if (havepattern && regexec(&regexbuf, iterator->file, 0, NULL, 0))
+ continue;
+
+ ast_cli(fd, FORMAT, iterator->file, iterator->version);
+ count_files++;
+ if (havename)
+ break;
+ }
+ AST_LIST_UNLOCK(&file_versions);
+ if (!havename) {
+ ast_cli(fd, "%d files listed.\n", count_files);
+ }
+
+ if (havepattern)
+ regfree(&regexbuf);
+
+ return RESULT_SUCCESS;
+#undef FORMAT
+}
+
+static char *complete_show_version_files_deprecated(const char *line, const char *word, int pos, int state)
+{
+ struct file_version *find;
+ int which = 0;
+ char *ret = NULL;
+ int matchlen = strlen(word);
+
+ if (pos != 3)
+ return NULL;
+
+ AST_LIST_LOCK(&file_versions);
+ AST_LIST_TRAVERSE(&file_versions, find, list) {
+ if (!strncasecmp(word, find->file, matchlen) && ++which > state) {
+ ret = ast_strdup(find->file);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&file_versions);
+
+ return ret;
+}
+
+static char *complete_show_version_files(const char *line, const char *word, int pos, int state)
+{
+ struct file_version *find;
+ int which = 0;
+ char *ret = NULL;
+ int matchlen = strlen(word);
+
+ if (pos != 4)
+ return NULL;
+
+ AST_LIST_LOCK(&file_versions);
+ AST_LIST_TRAVERSE(&file_versions, find, list) {
+ if (!strncasecmp(word, find->file, matchlen) && ++which > state) {
+ ret = ast_strdup(find->file);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&file_versions);
+
+ return ret;
+}
+
+#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_LIST_LOCK(&atexits);
+ AST_LIST_INSERT_HEAD(&atexits, ae, list);
+ AST_LIST_UNLOCK(&atexits);
+
+ return 0;
+}
+
+void ast_unregister_atexit(void (*func)(void))
+{
+ struct ast_atexit *ae = NULL;
+
+ AST_LIST_LOCK(&atexits);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&atexits, ae, list) {
+ if (ae->func == func) {
+ AST_LIST_REMOVE_CURRENT(&atexits, list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&atexits);
+
+ if (ae)
+ free(ae);
+}
+
+/* Sending commands from consoles back to the daemon requires a terminating NULL */
+static int fdsend(int fd, const char *s)
+{
+ return write(fd, s, strlen(s) + 1);
+}
+
+/* Sending messages from the daemon back to the display requires _excluding_ the terminating NULL */
+static int fdprint(int fd, const char *s)
+{
+ return write(fd, s, strlen(s));
+}
+
+/*! \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, ASTERISK_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;
+ }
+ }
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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];
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ 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_background(&consoles[x].t, &attr, 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) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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, (char *)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) {
+ if (write(sig_alert_pipe[1], &a, sizeof(a)) < 0) {
+ fprintf(stderr, "hup_handler: write() failed: %s\n", strerror(errno));
+ }
+ }
+ 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 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_LIST_LOCK(&atexits);
+ AST_LIST_TRAVERSE(&atexits, ae, list) {
+ if (ae->func)
+ ae->func();
+ }
+ AST_LIST_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);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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("%s", 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) {
+ if (write(sig_alert_pipe[1], &a, sizeof(a)) < 0) {
+ fprintf(stderr, "hup_handler: write() failed: %s\n", strerror(errno));
+ }
+ }
+ /* There is no need to restore the signal handler here, since the app
+ * is going to exit */
+}
+
+static void __remote_quit_handler(int num)
+{
+ sig_flags.need_quit = 1;
+}
+
+static const char *fix_header(char *outbuf, int maxout, const char *s, char *cmp)
+{
+ const char *c;
+
+ /* Check for verboser preamble */
+ if (*s == 127) {
+ s++;
+ }
+
+ 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 {
+ if (*s == 127) {
+ s++;
+ }
+ 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("%s", 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 abort_halt_help[] =
+"Usage: abort shutdown\n"
+" Causes Asterisk to abort an executing shutdown or restart, and resume normal\n"
+" call operations.\n";
+
+static char shutdown_now_help[] =
+"Usage: stop now\n"
+" Shuts down a running Asterisk immediately, hanging up all active calls .\n";
+
+static char shutdown_gracefully_help[] =
+"Usage: stop gracefully\n"
+" Causes Asterisk to not accept new calls, and exit when all\n"
+" active calls have terminated normally.\n";
+
+static char shutdown_when_convenient_help[] =
+"Usage: stop when convenient\n"
+" Causes Asterisk to perform a shutdown when all active calls have ended.\n";
+
+static char restart_now_help[] =
+"Usage: restart now\n"
+" Causes Asterisk to hangup all calls and exec() itself performing a cold\n"
+" restart.\n";
+
+static char restart_gracefully_help[] =
+"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";
+
+static char restart_when_convenient_help[] =
+"Usage: restart when convenient\n"
+" Causes Asterisk to perform a cold restart when all active calls have ended.\n";
+
+static char bang_help[] =
+"Usage: !<command>\n"
+" Executes a given shell command\n";
+
+static char show_warranty_help[] =
+"Usage: core show warranty\n"
+" Shows the warranty (if any) for this copy of Asterisk.\n";
+
+static char show_license_help[] =
+"Usage: core show license\n"
+" Shows the license(s) for this copy of Asterisk.\n";
+
+static char version_help[] =
+"Usage: core show version\n"
+" Shows Asterisk version information.\n";
+
+static int handle_version_deprecated(int fd, int argc, char *argv[])
+{
+ if (argc != 2)
+ return RESULT_SHOWUSAGE;
+ ast_cli(fd, "Asterisk %s built by %s @ %s on a %s running %s on %s\n",
+ ASTERISK_VERSION, ast_build_user, ast_build_hostname,
+ ast_build_machine, ast_build_os, ast_build_date);
+ return RESULT_SUCCESS;
+}
+
+static int handle_version(int fd, int argc, char *argv[])
+{
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ ast_cli(fd, "Asterisk %s built by %s @ %s on a %s running %s on %s\n",
+ ASTERISK_VERSION, ast_build_user, ast_build_hostname,
+ ast_build_machine, ast_build_os, ast_build_date);
+ return RESULT_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 int handle_shutdown_now(int fd, int argc, char *argv[])
+{
+ if (argc != 2)
+ return RESULT_SHOWUSAGE;
+ quit_handler(0, 0 /* Not nice */, 1 /* safely */, 0 /* not restart */);
+ return RESULT_SUCCESS;
+}
+
+static int handle_shutdown_gracefully(int fd, int argc, char *argv[])
+{
+ if (argc != 2)
+ return RESULT_SHOWUSAGE;
+ quit_handler(0, 1 /* nicely */, 1 /* safely */, 0 /* no restart */);
+ return RESULT_SUCCESS;
+}
+
+static int handle_shutdown_when_convenient(int fd, int argc, char *argv[])
+{
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ ast_cli(fd, "Waiting for inactivity to perform halt\n");
+ quit_handler(0, 2 /* really nicely */, 1 /* safely */, 0 /* don't restart */);
+ return RESULT_SUCCESS;
+}
+
+static int handle_restart_now(int fd, int argc, char *argv[])
+{
+ if (argc != 2)
+ return RESULT_SHOWUSAGE;
+ quit_handler(0, 0 /* not nicely */, 1 /* safely */, 1 /* restart */);
+ return RESULT_SUCCESS;
+}
+
+static int handle_restart_gracefully(int fd, int argc, char *argv[])
+{
+ if (argc != 2)
+ return RESULT_SHOWUSAGE;
+ quit_handler(0, 1 /* nicely */, 1 /* safely */, 1 /* restart */);
+ return RESULT_SUCCESS;
+}
+
+static int handle_restart_when_convenient(int fd, int argc, char *argv[])
+{
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ ast_cli(fd, "Waiting for inactivity to perform restart\n");
+ quit_handler(0, 2 /* really nicely */, 1 /* safely */, 1 /* restart */);
+ return RESULT_SUCCESS;
+}
+
+static int handle_abort_halt(int fd, int argc, char *argv[])
+{
+ if (argc != 2)
+ return RESULT_SHOWUSAGE;
+ ast_cancel_shutdown();
+ shuttingdown = 0;
+ return RESULT_SUCCESS;
+}
+
+static int handle_bang(int fd, int argc, char *argv[])
+{
+ return RESULT_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 int show_warranty(int fd, int argc, char *argv[])
+{
+ int x;
+
+ for (x = 0; x < ARRAY_LEN(warranty_lines); x++)
+ ast_cli(fd, "%s", (char *) warranty_lines[x]);
+
+ return RESULT_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 int show_license(int fd, int argc, char *argv[])
+{
+ int x;
+
+ for (x = 0; x < ARRAY_LEN(license_lines); x++)
+ ast_cli(fd, "%s", (char *) license_lines[x]);
+
+ return RESULT_SUCCESS;
+}
+
+#define ASTERISK_PROMPT "*CLI> "
+
+#define ASTERISK_PROMPT2 "%s*CLI> "
+
+static struct ast_cli_entry cli_show_version_deprecated = {
+ { "show", "version", NULL },
+ handle_version_deprecated, "Display version info",
+ version_help };
+
+#if !defined(LOW_MEMORY)
+static struct ast_cli_entry cli_show_version_files_deprecated = {
+ { "show", "version", "files", NULL },
+ handle_show_version_files_deprecated, NULL,
+ NULL, complete_show_version_files_deprecated };
+
+static struct ast_cli_entry cli_show_profile_deprecated = {
+ { "show", "profile", NULL },
+ handle_show_profile_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_clear_profile_deprecated = {
+ { "clear", "profile", NULL },
+ handle_show_profile_deprecated, NULL,
+ NULL };
+#endif /* ! LOW_MEMORY */
+
+static struct ast_cli_entry cli_asterisk[] = {
+ { { "abort", "halt", NULL },
+ handle_abort_halt, "Cancel a running halt",
+ abort_halt_help },
+
+ { { "stop", "now", NULL },
+ handle_shutdown_now, "Shut down Asterisk immediately",
+ shutdown_now_help },
+
+ { { "stop", "gracefully", NULL },
+ handle_shutdown_gracefully, "Gracefully shut down Asterisk",
+ shutdown_gracefully_help },
+
+ { { "stop", "when", "convenient", NULL },
+ handle_shutdown_when_convenient, "Shut down Asterisk at empty call volume",
+ shutdown_when_convenient_help },
+
+ { { "restart", "now", NULL },
+ handle_restart_now, "Restart Asterisk immediately", restart_now_help },
+
+ { { "restart", "gracefully", NULL },
+ handle_restart_gracefully, "Restart Asterisk gracefully",
+ restart_gracefully_help },
+
+ { { "restart", "when", "convenient", NULL },
+ handle_restart_when_convenient, "Restart Asterisk at empty call volume",
+ restart_when_convenient_help },
+
+ { { "core", "show", "warranty", NULL },
+ show_warranty, "Show the warranty (if any) for this copy of Asterisk",
+ show_warranty_help },
+
+ { { "core", "show", "license", NULL },
+ show_license, "Show the license(s) for this copy of Asterisk",
+ show_license_help },
+
+ { { "core", "show", "version", NULL },
+ handle_version, "Display version info",
+ version_help, NULL, &cli_show_version_deprecated },
+
+ { { "!", NULL },
+ handle_bang, "Execute a shell command",
+ bang_help },
+
+#if !defined(LOW_MEMORY)
+ { { "core", "show", "file", "version", NULL },
+ handle_show_version_files, "List versions of files used to build Asterisk",
+ show_version_files_help, complete_show_version_files, &cli_show_version_files_deprecated },
+
+ { { "core", "show", "threads", NULL },
+ handle_show_threads, "Show running threads",
+ show_threads_help },
+
+ { { "core", "show", "profile", NULL },
+ handle_show_profile, "Display profiling info",
+ NULL, NULL, &cli_show_profile_deprecated },
+
+ { { "core", "clear", "profile", NULL },
+ handle_show_profile, "Clear profiling info",
+ NULL, NULL, &cli_clear_profile_deprecated },
+#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 (sig_flags.need_quit)
+ break;
+ 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) {
+ char *tmp;
+ 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("%s", term_quit());
+ WELCOME_MESSAGE;
+ if (!ast_opt_mute)
+ fdsend(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';
+
+ /* Strip preamble from asynchronous events, too */
+ for (tmp = buf; *tmp; tmp++) {
+ if (*tmp == 127) {
+ memmove(tmp, tmp + 1, strlen(tmp));
+ tmp--;
+ }
+ }
+
+ /* Write over the CLI prompt */
+ if (!ast_opt_exec && !lastpos) {
+ if (write(STDOUT_FILENO, "\r", 1) < 0) {
+ }
+ }
+ if (write(STDOUT_FILENO, buf, res) < 0) {
+ }
+ 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;
+ time_t ts;
+ struct tm tm;
+#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 */
+ if ((fgcolor == COLOR_WHITE) && (bgcolor == COLOR_BLACK)) {
+ color_used = 0;
+ } else {
+ color_used = 1;
+ }
+ break;
+ case 'd': /* date */
+ memset(&tm, 0, sizeof(tm));
+ time(&ts);
+ if (ast_localtime(&ts, &tm, NULL)) {
+ 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;
+
+ if (fscanf(LOADAVG, "%f %f %f %d/%d %d",
+ &avg1, &avg2, &avg3, &actproc, &totproc, &npid) != 6) {
+ ast_log(LOG_WARNING, "parsing /proc/loadavg failed\n");
+ fclose(LOADAVG);
+ break;
+ }
+ 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;
+ }
+ }
+ fclose(LOADAVG);
+ }
+ 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 */
+ memset(&tm, 0, sizeof(tm));
+ time(&ts);
+ if (ast_localtime(&ts, &tm, NULL)) {
+ 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) - 1) {
+ ast_copy_string(prompt + sizeof(prompt) - strlen(term_code) - 1, term_code, strlen(term_code) + 1);
+ } else {
+ /* This looks wrong, but we've already checked the length of term_code to ensure it's safe */
+ strncat(p, term_code, sizeof(term_code));
+ }
+ }
+ } else if (remotehostname)
+ snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT2, remotehostname);
+ else
+ snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT);
+
+ return(prompt);
+}
+
+static char **ast_el_strtoarr(char *buf)
+{
+ char **match_list = NULL, *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 = ast_realloc(match_list, match_list_len * sizeof(char *)))) {
+ /* TODO: Handle memory allocation failure */
+ }
+ }
+
+ match_list[matches++] = strdup(retstr);
+ }
+
+ if (!match_list)
+ return (char **) NULL;
+
+ if (matches >= match_list_len) {
+ if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(char *)))) {
+ /* TODO: Handle memory allocation failure */
+ }
+ }
+
+ 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--;
+ free(matches[idx]);
+ matches[idx] = NULL;
+ continue;
+ }
+
+ numoutput++;
+ numoutputline++;
+ fprintf(stdout, "%-*s ", max, matches[idx]);
+ 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);
+ fdsend(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);
+ fdsend(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);
+ 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++)
+ free(matches[i]);
+ 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)) {
+ if (!fgets(buf, sizeof(buf), f)) {
+ continue;
+ }
+ 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;
+
+ memset(&sig_flags, 0, sizeof(sig_flags));
+ signal(SIGINT, __remote_quit_handler);
+ signal(SIGTERM, __remote_quit_handler);
+ signal(SIGHUP, __remote_quit_handler);
+
+ if (read(ast_consock, buf, sizeof(buf)) < 0) {
+ ast_log(LOG_ERROR, "read() failed: %s\n", strerror(errno));
+ return;
+ }
+ if (data) {
+ if (write(ast_consock, data, strlen(data) + 1) < 0) {
+ ast_log(LOG_ERROR, "write() failed: %s\n", strerror(errno));
+ if (sig_flags.need_quit == 1) {
+ return;
+ }
+ }
+ }
+ 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);
+ fdsend(ast_consock, tmp);
+ snprintf(tmp, sizeof(tmp), "core set debug atleast %d", option_debug);
+ fdsend(ast_consock, tmp);
+ if (!ast_opt_mute)
+ fdsend(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 */
+ struct pollfd fds;
+ fds.fd = ast_consock;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ while (poll(&fds, 1, 500) > 0) {
+ char buf[512] = "", *curline = buf, *nextline;
+ int not_written = 1;
+
+ if (sig_flags.need_quit == 1) {
+ break;
+ }
+
+ if (read(ast_consock, buf, sizeof(buf) - 1) <= 0) {
+ break;
+ }
+
+ do {
+ if ((nextline = strchr(curline, '\n'))) {
+ nextline++;
+ } else {
+ nextline = strchr(curline, '\0');
+ }
+
+ /* Skip verbose lines */
+ if (*curline != 127) {
+ not_written = 0;
+ if (write(STDOUT_FILENO, curline, nextline - curline) < 0) {
+ ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
+ }
+ }
+ curline = nextline;
+ } while (!ast_strlen_zero(curline));
+
+ /* No non-verbose output in 500ms */
+ if (not_written) {
+ break;
+ }
+ }
+ return;
+ }
+ for (;;) {
+ ebuf = (char *)el_gets(el, &num);
+
+ if (sig_flags.need_quit == 1) {
+ break;
+ }
+
+ 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)) {
+ /* Strip preamble from output */
+ char *tmp;
+ for (tmp = ebuf; *tmp; tmp++) {
+ if (*tmp == 127) {
+ memmove(tmp, tmp + 1, strlen(tmp));
+ tmp--;
+ }
+ }
+ 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 " ASTERISK_VERSION "\n");
+ return 0;
+}
+
+static int show_cli_help(void) {
+ printf("Asterisk " ASTERISK_VERSION ", Copyright (C) 1999 - 2008, Digium, Inc. and others.\n");
+ 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 %s timer is available\n", dahdi_chan_name);
+ 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("\n");
+ return 0;
+}
+
+static void ast_readconfig(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ char *config = AST_CONFIG_FILE;
+
+ if (ast_opt_override_config) {
+ cfg = ast_config_load(ast_config_AST_CONFIG_FILE);
+ 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);
+ }
+
+ /* init with buildtime config */
+ ast_copy_string(ast_config_AST_CONFIG_DIR, AST_CONFIG_DIR, sizeof(ast_config_AST_CONFIG_DIR));
+ ast_copy_string(ast_config_AST_SPOOL_DIR, AST_SPOOL_DIR, sizeof(ast_config_AST_SPOOL_DIR));
+ ast_copy_string(ast_config_AST_MODULE_DIR, AST_MODULE_DIR, sizeof(ast_config_AST_MODULE_DIR));
+ snprintf(ast_config_AST_MONITOR_DIR, sizeof(ast_config_AST_MONITOR_DIR) - 1, "%s/monitor", ast_config_AST_SPOOL_DIR);
+ ast_copy_string(ast_config_AST_VAR_DIR, AST_VAR_DIR, sizeof(ast_config_AST_VAR_DIR));
+ ast_copy_string(ast_config_AST_DATA_DIR, AST_DATA_DIR, sizeof(ast_config_AST_DATA_DIR));
+ ast_copy_string(ast_config_AST_LOG_DIR, AST_LOG_DIR, sizeof(ast_config_AST_LOG_DIR));
+ ast_copy_string(ast_config_AST_AGI_DIR, AST_AGI_DIR, sizeof(ast_config_AST_AGI_DIR));
+ ast_copy_string(ast_config_AST_DB, AST_DB, sizeof(ast_config_AST_DB));
+ ast_copy_string(ast_config_AST_KEY_DIR, AST_KEY_DIR, sizeof(ast_config_AST_KEY_DIR));
+ ast_copy_string(ast_config_AST_PID, AST_PID, sizeof(ast_config_AST_PID));
+ ast_copy_string(ast_config_AST_SOCKET, AST_SOCKET, sizeof(ast_config_AST_SOCKET));
+ ast_copy_string(ast_config_AST_RUN_DIR, AST_RUN_DIR, sizeof(ast_config_AST_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(ast_config_AST_CONFIG_DIR, v->value, sizeof(ast_config_AST_CONFIG_DIR));
+ } else if (!strcasecmp(v->name, "astspooldir")) {
+ ast_copy_string(ast_config_AST_SPOOL_DIR, v->value, sizeof(ast_config_AST_SPOOL_DIR));
+ snprintf(ast_config_AST_MONITOR_DIR, sizeof(ast_config_AST_MONITOR_DIR) - 1, "%s/monitor", v->value);
+ } else if (!strcasecmp(v->name, "astvarlibdir")) {
+ ast_copy_string(ast_config_AST_VAR_DIR, v->value, sizeof(ast_config_AST_VAR_DIR));
+ snprintf(ast_config_AST_DB, sizeof(ast_config_AST_DB), "%s/astdb", v->value);
+ } else if (!strcasecmp(v->name, "astdatadir")) {
+ ast_copy_string(ast_config_AST_DATA_DIR, v->value, sizeof(ast_config_AST_DATA_DIR));
+ snprintf(ast_config_AST_KEY_DIR, sizeof(ast_config_AST_KEY_DIR), "%s/keys", v->value);
+ } else if (!strcasecmp(v->name, "astlogdir")) {
+ ast_copy_string(ast_config_AST_LOG_DIR, v->value, sizeof(ast_config_AST_LOG_DIR));
+ } else if (!strcasecmp(v->name, "astagidir")) {
+ ast_copy_string(ast_config_AST_AGI_DIR, v->value, sizeof(ast_config_AST_AGI_DIR));
+ } else if (!strcasecmp(v->name, "astrundir")) {
+ snprintf(ast_config_AST_PID, sizeof(ast_config_AST_PID), "%s/%s", v->value, "asterisk.pid");
+ snprintf(ast_config_AST_SOCKET, sizeof(ast_config_AST_SOCKET), "%s/%s", v->value, ast_config_AST_CTL);
+ ast_copy_string(ast_config_AST_RUN_DIR, v->value, sizeof(ast_config_AST_RUN_DIR));
+ } else if (!strcasecmp(v->name, "astmoddir")) {
+ ast_copy_string(ast_config_AST_MODULE_DIR, v->value, sizeof(ast_config_AST_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 or DTMF is being generated on a channel */
+ } else if (!strcasecmp(v->name, "transmit_silence_during_record") || !strcasecmp(v->name, "transmit_silence")) {
+ 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;
+ }
+ /* What user to run as */
+ } else if (!strcasecmp(v->name, "runuser")) {
+ ast_copy_string(ast_config_AST_RUN_USER, v->value, sizeof(ast_config_AST_RUN_USER));
+ /* What group to run as */
+ } else if (!strcasecmp(v->name, "rungroup")) {
+ ast_copy_string(ast_config_AST_RUN_GROUP, v->value, sizeof(ast_config_AST_RUN_GROUP));
+ } else if (!strcasecmp(v->name, "systemname")) {
+ ast_copy_string(ast_config_AST_SYSTEM_NAME, v->value, sizeof(ast_config_AST_SYSTEM_NAME));
+ } else if (!strcasecmp(v->name, "languageprefix")) {
+ ast_language_is_prefix = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "dahdichanname")) {
+#ifdef HAVE_ZAPTEL
+ if (ast_true(v->value)) {
+ strcpy(_dahdi_chan_name, "DAHDI");
+ _dahdi_chan_name_len = 5;
+ _dahdi_chan_mode = CHAN_DAHDI_PLUS_ZAP_MODE;
+ }
+#else
+ if (ast_false(v->value)) {
+ strcpy(_dahdi_chan_name, "Zap");
+ _dahdi_chan_name_len = 3;
+ _dahdi_chan_mode = CHAN_ZAP_MODE;
+ }
+#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);
+ }
+ if (read(sig_alert_pipe[0], &a, sizeof(a)) != sizeof(a)) {
+ }
+ }
+
+ return NULL;
+}
+
+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;
+ char *runuser = NULL, *rungroup = 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:")) != -1) {
+ switch (c) {
+#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(ast_config_AST_CONFIG_FILE, optarg, sizeof(ast_config_AST_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 '?':
+ 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");
+
+ /* 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_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);
+ }
+
+ 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;
+
+#ifndef __CYGWIN__
+
+ if (isroot)
+ ast_set_priority(ast_opt_high_priority);
+
+ 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("%s", 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("%s", 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("%s", 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("%s", 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) {
+#ifndef HAVE_SBIN_LAUNCHD
+ if (daemon(1, 0) < 0) {
+ ast_log(LOG_ERROR, "daemon() failed: %s\n", strerror(errno));
+ }
+ 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));
+#else
+ ast_log(LOG_WARNING, "Mac OS X detected. Use '/sbin/launchd -d' to launch with the nofork option.\n");
+#endif
+ }
+#endif
+
+ 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(SIGCHLD, child_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()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
+ dahdi_chan_name = _dahdi_chan_name;
+ dahdi_chan_name_len = &_dahdi_chan_name_len;
+ dahdi_chan_mode = &_dahdi_chan_mode;
+
+#ifdef HAVE_DAHDI
+ {
+ int fd;
+ int x = 160;
+ fd = open(DAHDI_FILE_TIMER, O_RDWR);
+ if (fd >= 0) {
+ if (ioctl(fd, DAHDI_TIMERCONFIG, &x)) {
+ ast_log(LOG_ERROR, "You have " DAHDI_NAME
+ " built and drivers loaded, but the "
+ DAHDI_NAME " timer test failed to set DAHDI_TIMERCONFIG to %d.\n", x);
+ exit(1);
+ }
+ if ((x = ast_wait_for_input(fd, 300)) < 0) {
+ ast_log(LOG_ERROR, "You have " DAHDI_NAME
+ "built and drivers loaded, but the "
+ DAHDI_NAME " timer could not be polled during the "
+ DAHDI_NAME " timer test.\n");
+ exit(1);
+ }
+ if (!x) {
+ const char dahdi_timer_error[] = {
+ "Asterisk has detected a problem with your " DAHDI_NAME
+ " configuration and will shutdown for your protection. You have options:"
+ "\n\t1. You only have to compile " DAHDI_NAME
+ " support into Asterisk if you need it. One option is to recompile without "
+ DAHDI_NAME " support."
+ "\n\t2. You only have to load " DAHDI_NAME " drivers if you want to take advantage of "
+ DAHDI_NAME " services. One option is to unload "
+ DAHDI_NAME " modules if you don't need them."
+ "\n\t3. If you need Zaptel services, you must correctly configure " DAHDI_NAME "."
+ };
+ ast_log(LOG_ERROR, "%s\n", dahdi_timer_error);
+ exit(1);
+ }
+ close(fd);
+ }
+ }
+#endif
+ threadstorage_init();
+
+ astobj2_init();
+
+ ast_autoservice_init();
+
+ if (load_modules(1)) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
+ if (dnsmgr_init()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
+ ast_http_init();
+
+ ast_channels_init();
+
+ if (ast_cdr_engine_init()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
+ if (ast_device_state_engine_init()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
+ ast_rtp_init();
+
+ ast_udptl_init();
+
+ if (ast_image_init()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
+ if (ast_file_init()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
+ if (load_pbx()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
+ if (init_framer()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
+ if (astdb_init()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
+ if (ast_enum_init()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
+ if (load_modules(0)) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
+ /* AMI is initialized after loading modules because of a potential
+ * conflict between issuing a module reload from manager and
+ * registering manager actions. This will cause reversed locking
+ * order between the module list and manager actions list. */
+ if (init_manager()) {
+ printf("%s", 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("%s", 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
+
+ time(&ast_startuptime);
+ ast_cli_register_multiple(cli_asterisk, sizeof(cli_asterisk) / sizeof(struct ast_cli_entry));
+
+ if (ast_opt_console) {
+ /* Console stuff now... */
+ /* Register our quit function */
+ char title[256];
+ pthread_attr_t attr;
+ pthread_t dont_care;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ ast_pthread_create(&dont_care, &attr, monitor_sig_flags, NULL);
+ pthread_attr_destroy(&attr);
+
+ 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/main/astmm.c b/main/astmm.c
new file mode 100644
index 000000000..b0eb51bf5
--- /dev/null
+++ b/main/astmm.c
@@ -0,0 +1,498 @@
+/*
+ * 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>
+ */
+
+#include "asterisk.h"
+
+#ifdef __AST_DEBUG_MALLOC
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <time.h>
+
+#include "asterisk/cli.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.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;
+
+/* NOTE: Be EXTREMELY careful with modifying this structure; the total size of this structure
+ must result in 'automatic' alignment so that the 'fence' field lands exactly at the end of
+ the structure in memory (and thus immediately before the allocated region the fence is
+ supposed to be used to monitor). In other words, we cannot allow the compiler to insert
+ any padding between this structure and anything following it, so add up the sizes of all the
+ fields and compare to sizeof(struct ast_region)... if they don't match, then the compiler
+ is padding the structure and either the fields need to be rearranged to eliminate internal
+ padding, or a dummy field will need to be inserted before the 'fence' field to push it to
+ the end of the actual space it will consume. Note that this must be checked for both 32-bit
+ and 64-bit platforms, as the sizes of pointers and 'size_t' differ on these platforms.
+*/
+
+static struct ast_region {
+ struct ast_region *next;
+ size_t len;
+ 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 */
+ unsigned int fence;
+ unsigned char data[0];
+} *regions[SOME_PRIME];
+
+#define HASH(a) \
+ (((unsigned long)(a)) % SOME_PRIME)
+
+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 int handle_show_memory(int fd, int argc, char *argv[])
+{
+ 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;
+
+ if (argc > 3)
+ fn = 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(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(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
+ else
+ ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_show_memory_summary(int fd, int argc, char *argv[])
+{
+ 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;
+
+ if (argc > 3)
+ fn = 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(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(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(fd, "%10d bytes in %d allocations in function '%s' of '%s'\n",
+ cur->len, cur->count, cur->fn, fn);
+ } else {
+ ast_cli(fd, "%10d bytes in %d allocations in file '%s'\n",
+ cur->len, cur->count, cur->fn);
+ }
+ }
+ }
+
+ if (cache_len)
+ ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
+ else
+ ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
+
+ return RESULT_SUCCESS;
+}
+
+static char show_memory_help[] =
+"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";
+
+static char show_memory_summary_help[] =
+"Usage: memory show summary [<file>]\n"
+" Summarizes heap memory allocations by file, or optionally\n"
+"by function, if a file is specified\n";
+
+static struct ast_cli_entry cli_show_memory_allocations_deprecated = {
+ { "show", "memory", "allocations", NULL },
+ handle_show_memory, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_memory_summary_deprecated = {
+ { "show", "memory", "summary", NULL },
+ handle_show_memory_summary, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_memory[] = {
+ { { "memory", "show", "allocations", NULL },
+ handle_show_memory, "Display outstanding memory allocations",
+ show_memory_help, NULL, &cli_show_memory_allocations_deprecated },
+
+ { { "memory", "show", "summary", NULL },
+ handle_show_memory_summary, "Summarize outstanding memory allocations",
+ show_memory_summary_help, NULL, &cli_show_memory_summary_deprecated },
+};
+
+void __ast_mm_init(void)
+{
+ char filename[PATH_MAX];
+ size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
+
+ if (pad) {
+ ast_log(LOG_ERROR, "struct ast_region has %d bytes of padding! This must be eliminated for low-fence checking to work properly!\n", (int) pad);
+ }
+
+ ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
+
+ snprintf(filename, sizeof(filename), "%s/mmlog", (char *)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/main/astobj2.c b/main/astobj2.c
new file mode 100644
index 000000000..56ab75a21
--- /dev/null
+++ b/main/astobj2.c
@@ -0,0 +1,771 @@
+/*
+ * 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/astobj2.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+
+/*!
+ * astobj2 objects are always prepended 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)
+
+#ifndef DEBUG_THREADS
+int ao2_lock(void *user_data)
+#else
+int _ao2_lock(void *user_data, const char *file, const char *func, int line, const char *var)
+#endif
+{
+ struct astobj2 *p = INTERNAL_OBJ(user_data);
+
+ if (p == NULL)
+ return -1;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+#endif
+
+#ifndef DEBUG_THREADS
+ return ast_mutex_lock(&p->priv_data.lock);
+#else
+ return __ast_pthread_mutex_lock(file, line, func, var, &p->priv_data.lock);
+#endif
+}
+
+#ifndef DEBUG_THREADS
+int ao2_trylock(void *user_data)
+#else
+int _ao2_trylock(void *user_data, const char *file, const char *func, int line, const char *var)
+#endif
+{
+ struct astobj2 *p = INTERNAL_OBJ(user_data);
+ int res;
+
+ if (p == NULL)
+ return -1;
+
+#ifndef DEBUG_THREADS
+ res = ast_mutex_trylock(&p->priv_data.lock);
+#else
+ res = __ast_pthread_mutex_trylock(file, line, func, var, &p->priv_data.lock);
+#endif
+
+#ifdef AO2_DEBUG
+ if (!res) {
+ ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+ }
+#endif
+
+ return res;
+}
+
+#ifndef DEBUG_THREADS
+int ao2_unlock(void *user_data)
+#else
+int _ao2_unlock(void *user_data, const char *file, const char *func, int line, const char *var)
+#endif
+{
+ struct astobj2 *p = INTERNAL_OBJ(user_data);
+
+ if (p == NULL)
+ return -1;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_locked, -1);
+#endif
+
+#ifndef DEBUG_THREADS
+ return ast_mutex_unlock(&p->priv_data.lock);
+#else
+ return __ast_pthread_mutex_unlock(file, line, func, var, &p->priv_data.lock);
+#endif
+}
+
+/*
+ * 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 unsigned int 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 iax2_hack)
+{
+ 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);
+ if (iax2_hack)
+ AST_LIST_INSERT_HEAD(&c->buckets[i], p, entry);
+ else
+ 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 0
+ /* Removing this slightly changes the meaning of OBJ_POINTER, but makes it
+ * do what I want it to. I'd like to hint to ao2_callback that the arg is
+ * of the same object type, so it can be passed to the hash function.
+ * However, I don't want to imply that this is the object being searched for. */
+ if (flags & OBJ_POINTER)
+ cb_fn = match_by_addr;
+ else
+#endif
+ 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(&c->buckets[i], 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;
+ int i;
+
+ ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
+
+ for (i = 0; i < c->n_buckets; i++) {
+ struct bucket_list *cur;
+
+ while ((cur = AST_LIST_REMOVE_HEAD(&c->buckets[i], entry))) {
+ ast_free(cur);
+ }
+ }
+
+#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 int handle_astobj2_stats(int fd, int argc, char *argv[])
+{
+ ast_cli(fd, "Objects : %d\n", ao2.total_objects);
+ ast_cli(fd, "Containers : %d\n", ao2.total_containers);
+ ast_cli(fd, "Memory : %d\n", ao2.total_mem);
+ ast_cli(fd, "Locked : %d\n", ao2.total_locked);
+ ast_cli(fd, "Refs : %d\n", ao2.total_refs);
+ return 0;
+}
+
+/*
+ * This is testing code for astobj
+ */
+static int handle_astobj2_test(int fd, int argc, char *argv[])
+{
+ struct ao2_container *c1;
+ int i, lim;
+ char *obj;
+ static int prof_id = -1;
+
+ if (prof_id == -1)
+ prof_id = ast_add_profile("ao2_alloc", 0);
+
+ ast_cli(fd, "argc %d argv %s %s %s\n", argc, argv[0], argv[1], argv[2]);
+ lim = atoi(argv[2]);
+ ast_cli(fd, "called astobj_test\n");
+
+ handle_astobj2_stats(fd, 0, NULL);
+ /*
+ * 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(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(fd, "object %d allocated as %p\n", i, obj);
+ sprintf(obj, "-- this is obj %d --", i);
+ ao2_link(c1, obj);
+ }
+ ast_cli(fd, "testing callbacks\n");
+ ao2_callback(c1, 0, print_cb, &fd);
+
+ ast_cli(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(fd, "iterator on <%s>\n", obj);
+ if (x++ & 1)
+ ao2_unlink(c1, obj);
+ ao2_ref(obj, -1);
+ }
+ ast_cli(fd, "testing iterators again\n");
+ ai = ao2_iterator_init(c1, 0);
+ while ( (obj = ao2_iterator_next(&ai)) ) {
+ ast_cli(fd, "iterator on <%s>\n", obj);
+ ao2_ref(obj, -1);
+ }
+ }
+ ast_cli(fd, "testing callbacks again\n");
+ ao2_callback(c1, 0, print_cb, &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(fd, "destroy container\n");
+ ao2_ref(c1, -1); /* destroy container */
+ handle_astobj2_stats(fd, 0, NULL);
+ return 0;
+}
+
+static struct ast_cli_entry cli_astobj2[] = {
+ { { "astobj2", "stats", NULL },
+ handle_astobj2_stats, "Print astobj2 statistics", },
+ { { "astobj2", "test", NULL } , 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/main/audiohook.c b/main/audiohook.c
new file mode 100644
index 000000000..cd5b7ca8a
--- /dev/null
+++ b/main/audiohook.c
@@ -0,0 +1,721 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/options.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);
+ struct ast_slinfactory *other_factory = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook->write_factory : &audiohook->read_factory);
+ struct timeval *time = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook->read_time : &audiohook->write_time), previous_time = *time;
+ int our_factory_ms;
+ int other_factory_samples;
+ int other_factory_ms;
+
+ /* Update last feeding time to be current */
+ *time = ast_tvnow();
+
+ our_factory_ms = ast_tvdiff_ms(*time, previous_time) + (ast_slinfactory_available(factory) / 8);
+ other_factory_samples = ast_slinfactory_available(other_factory);
+ other_factory_ms = other_factory_samples / 8;
+
+ /* If we are using a sync trigger and this factory suddenly got audio fed in after a lapse, then flush both factories to ensure they remain in sync */
+ if (ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC) && other_factory_samples && (our_factory_ms - other_factory_ms > AST_AUDIOHOOK_SYNC_TOLERANCE)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Flushing audiohook %p so it remains in sync\n", audiohook);
+ ast_slinfactory_flush(factory);
+ ast_slinfactory_flush(other_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 */
+ if ((ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_MODE) == AST_AUDIOHOOK_TRIGGER_READ) && (direction == AST_AUDIOHOOK_DIRECTION_READ)) {
+ ast_cond_signal(&audiohook->trigger);
+ } else if ((ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_MODE) == AST_AUDIOHOOK_TRIGGER_WRITE) && (direction == AST_AUDIOHOOK_DIRECTION_WRITE)) {
+ ast_cond_signal(&audiohook->trigger);
+ } else if (ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC)) {
+ ast_cond_signal(&audiohook->trigger);
+ }
+
+ 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, usable_read, usable_write;
+ 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,
+ };
+
+ /* Make sure both factories have the required samples */
+ usable_read = (ast_slinfactory_available(&audiohook->read_factory) >= samples ? 1 : 0);
+ usable_write = (ast_slinfactory_available(&audiohook->write_factory) >= samples ? 1 : 0);
+
+ if (!usable_read && !usable_write) {
+ /* If both factories are unusable bail out */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Read factory %p and write factory %p both fail to provide %zd samples\n", &audiohook->read_factory, &audiohook->write_factory, samples);
+ return NULL;
+ }
+
+ /* If we want to provide only a read factory make sure we aren't waiting for other audio */
+ if (usable_read && !usable_write && (ast_tvdiff_ms(ast_tvnow(), audiohook->write_time) < (samples/8)*2)) {
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Write factory %p was pretty quick last time, waiting for them.\n", &audiohook->write_factory);
+ return NULL;
+ }
+
+ /* If we want to provide only a write factory make sure we aren't waiting for other audio */
+ if (usable_write && !usable_read && (ast_tvdiff_ms(ast_tvnow(), audiohook->read_time) < (samples/8)*2)) {
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Read factory %p was pretty quick last time, waiting for them.\n", &audiohook->read_factory);
+ return NULL;
+ }
+
+ /* Start with the read factory... if there are enough samples, read them in */
+ if (usable_read) {
+ 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 %zd samples from read factory %p\n", samples, &audiohook->read_factory);
+
+ /* Move on to the write factory... if there are enough samples, read them in */
+ if (usable_write) {
+ 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 %zd samples from write factory %p\n", 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 */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->spy_list, audiohook, list) {
+ ast_audiohook_lock(audiohook);
+ AST_LIST_REMOVE_CURRENT(&audiohook_list->spy_list, list);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_cond_signal(&audiohook->trigger);
+ ast_audiohook_unlock(audiohook);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ /* Drop any whispering sources */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->whisper_list, audiohook, list) {
+ ast_audiohook_lock(audiohook);
+ AST_LIST_REMOVE_CURRENT(&audiohook_list->whisper_list, list);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_cond_signal(&audiohook->trigger);
+ ast_audiohook_unlock(audiohook);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ /* Drop any manipulaters */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) {
+ ast_audiohook_lock(audiohook);
+ AST_LIST_REMOVE_CURRENT(&audiohook_list->manipulate_list, list);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_audiohook_unlock(audiohook);
+ audiohook->manipulate_callback(audiohook, NULL, NULL, 0);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ /* 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;
+}
+
+void ast_audiohook_move_by_source (struct ast_channel *old_chan, struct ast_channel *new_chan, const char *source)
+{
+ struct ast_audiohook *audiohook = find_audiohook_by_source(old_chan->audiohooks, source);
+
+ if (!audiohook) {
+ return;
+ }
+
+ /* By locking both channels and the audiohook, we can assure that
+ * another thread will not have a chance to read the audiohook's status
+ * as done, even though ast_audiohook_remove signals the trigger
+ * condition
+ */
+ ast_audiohook_lock(audiohook);
+ ast_audiohook_remove(old_chan, audiohook);
+ ast_audiohook_attach(new_chan, audiohook);
+ ast_audiohook_unlock(audiohook);
+}
+
+/*! \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 Remove an audiohook from a specified channel
+ *
+ * \param chan Channel to remove from
+ * \param audiohook Audiohook to remove
+ *
+ * \return Returns 0 on success, -1 on failure
+ *
+ * \note The channel does not need to be locked before calling this function
+ */
+int ast_audiohook_remove(struct ast_channel *chan, struct ast_audiohook *audiohook)
+{
+ ast_channel_lock(chan);
+
+ if (!chan->audiohooks) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ if (audiohook->type == AST_AUDIOHOOK_TYPE_SPY)
+ AST_LIST_REMOVE(&chan->audiohooks->spy_list, audiohook, list);
+ else if (audiohook->type == AST_AUDIOHOOK_TYPE_WHISPER)
+ AST_LIST_REMOVE(&chan->audiohooks->whisper_list, audiohook, list);
+ else if (audiohook->type == AST_AUDIOHOOK_TYPE_MANIPULATE)
+ AST_LIST_REMOVE(&chan->audiohooks->manipulate_list, audiohook, list);
+
+ ast_audiohook_lock(audiohook);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_cond_signal(&audiohook->trigger);
+ ast_audiohook_unlock(audiohook);
+
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
+/*! \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(&audiohook_list->manipulate_list, 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(&audiohook_list->spy_list, 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(&audiohook_list->whisper_list, 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(&audiohook_list->manipulate_list, 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;
+}
diff --git a/main/autoservice.c b/main/autoservice.c
new file mode 100644
index 000000000..1150c4c7f
--- /dev/null
+++ b/main/autoservice.c
@@ -0,0 +1,319 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2008, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * 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 Automatic channel service routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Russell Bryant <russell@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "asterisk/pbx.h"
+#include "asterisk/frame.h"
+#include "asterisk/sched.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/logger.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;
+ /*! Frames go on at the head of deferred_frames, so we have the frames
+ * from newest to oldest. As we put them at the head of the readq, we'll
+ * end up with them in the right order for the channel's readq. */
+ AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
+ AST_LIST_ENTRY(asent) list;
+};
+
+static AST_LIST_HEAD_STATIC(aslist, asent);
+static ast_cond_t as_cond;
+
+static pthread_t asthread = AST_PTHREADT_NULL;
+
+static int as_chan_list_state;
+
+static void *autoservice_run(void *ign)
+{
+ for (;;) {
+ struct ast_channel *mons[MAX_AUTOMONS];
+ struct asent *ents[MAX_AUTOMONS];
+ struct ast_channel *chan;
+ struct asent *as;
+ int i, x = 0, ms = 50;
+ struct ast_frame *f = NULL;
+ struct ast_frame *defer_frame = NULL;
+
+ AST_LIST_LOCK(&aslist);
+
+ /* At this point, we know that no channels that have been removed are going
+ * to get used again. */
+ as_chan_list_state++;
+
+ if (AST_LIST_EMPTY(&aslist)) {
+ ast_cond_wait(&as_cond, &aslist.lock);
+ }
+
+ AST_LIST_TRAVERSE(&aslist, as, list) {
+ if (!as->chan->_softhangup) {
+ if (x < MAX_AUTOMONS) {
+ ents[x] = as;
+ mons[x++] = as->chan;
+ } else {
+ ast_log(LOG_WARNING, "Exceeded maximum number of automatic monitoring events. Fix autoservice.c\n");
+ }
+ }
+ }
+
+ AST_LIST_UNLOCK(&aslist);
+
+ if (!x) {
+ continue;
+ }
+
+ chan = ast_waitfor_n(mons, x, &ms);
+ if (!chan) {
+ continue;
+ }
+
+ 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 = &hangup_frame;
+ } else {
+
+ /* 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 = 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 (defer_frame) {
+ for (i = 0; i < x; i++) {
+ struct ast_frame *dup_f;
+
+ if (mons[i] != chan) {
+ continue;
+ }
+
+ if ((dup_f = ast_frdup(defer_frame))) {
+ AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
+ }
+
+ 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;
+
+ /* Check if the channel already has autoservice */
+ AST_LIST_LOCK(&aslist);
+ AST_LIST_TRAVERSE(&aslist, as, list) {
+ if (as->chan == chan) {
+ as->use_count++;
+ break;
+ }
+ }
+ AST_LIST_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_LIST_LOCK(&aslist);
+
+ if (AST_LIST_EMPTY(&aslist) && asthread != AST_PTHREADT_NULL) {
+ ast_cond_signal(&as_cond);
+ }
+
+ AST_LIST_INSERT_HEAD(&aslist, as, list);
+
+ 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_LIST_REMOVE(&aslist, as, list);
+ free(as);
+ asthread = AST_PTHREADT_NULL;
+ res = -1;
+ } else {
+ pthread_kill(asthread, SIGURG);
+ }
+ }
+
+ AST_LIST_UNLOCK(&aslist);
+
+ return res;
+}
+
+int ast_autoservice_stop(struct ast_channel *chan)
+{
+ int res = -1;
+ struct asent *as, *removed = NULL;
+ struct ast_frame *f;
+ int chan_list_state;
+
+ AST_LIST_LOCK(&aslist);
+
+ /* Save the autoservice channel list state. We _must_ verify that the channel
+ * list has been rebuilt before we return. Because, after we return, the channel
+ * could get destroyed and we don't want our poor autoservice thread to step on
+ * it after its gone! */
+ chan_list_state = as_chan_list_state;
+
+ /* Find the entry, but do not free it because it still can be in the
+ autoservice thread array */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {
+ if (as->chan == chan) {
+ as->use_count--;
+ if (as->use_count < 1) {
+ AST_LIST_REMOVE_CURRENT(&aslist, list);
+ removed = as;
+ }
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ if (removed && asthread != AST_PTHREADT_NULL) {
+ pthread_kill(asthread, SIGURG);
+ }
+
+ AST_LIST_UNLOCK(&aslist);
+
+ if (!removed) {
+ return 0;
+ }
+
+ /* Wait while autoservice thread rebuilds its list. */
+ while (chan_list_state == as_chan_list_state) {
+ usleep(1000);
+ }
+
+ /* Now autoservice thread should have no references to our entry
+ and we can safely destroy it */
+
+ if (!chan->_softhangup) {
+ res = 0;
+ }
+
+ if (!as->orig_end_dtmf_flag) {
+ ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
+ }
+
+ ast_channel_lock(chan);
+ while ((f = AST_LIST_REMOVE_HEAD(&as->deferred_frames, frame_list))) {
+ ast_queue_frame_head(chan, f);
+ ast_frfree(f);
+ }
+ ast_channel_unlock(chan);
+
+ free(as);
+
+ return res;
+}
+
+void ast_autoservice_init(void)
+{
+ ast_cond_init(&as_cond, NULL);
+}
diff --git a/main/buildinfo.c b/main/buildinfo.c
new file mode 100644
index 000000000..964e06eb3
--- /dev/null
+++ b/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/main/callerid.c b/main/callerid.c
new file mode 100644
index 000000000..d446ecbb8
--- /dev/null
+++ b/main/callerid.c
@@ -0,0 +1,1117 @@
+/*
+ * 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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.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/logger.h"
+#include "asterisk/fskmodem.h"
+#include "asterisk/options.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.spb = 7.0; /* 1200 baud */
+ /* cid->fskd.hdlc = 0; */ /* Async */
+ cid->fskd.nbit = 8; /* 8 bits */
+ cid->fskd.nstop = 1.0; /* 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; */
+ }
+
+ 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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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;
+ short *obuf;
+
+ if (!(buf = ast_calloc(1, 2 * len + cid->oldlen))) {
+ return -1;
+ }
+
+ obuf = buf;
+ 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_serie(&cid->fskd, buf, &mylen, &b);
+
+ if (mylen < 0) {
+ ast_log(LOG_ERROR, "No start bit found in fsk data.\n");
+ free(obuf);
+ return -1;
+ }
+
+ buf += (olen - mylen);
+
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "fsk_serie failed\n");
+ free(obuf);
+ return -1;
+ }
+
+ if (res == 1) {
+
+ b2 = b ;
+ 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" ) ;
+ free(obuf);
+ 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" ) ;
+ free(obuf);
+ 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 */
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "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 */
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "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;
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "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 */
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "did info:#2=%X\n", cid->rawdata[x]);
+ break ;
+ }
+ x++;
+ break ;
+ }
+ }
+ free(obuf);
+ 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;
+ free(obuf);
+ 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;
+ short *obuf;
+
+ if (!(buf = ast_calloc(1, 2 * len + cid->oldlen))) {
+ return -1;
+ }
+
+ obuf = buf;
+ 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_serie(&cid->fskd, buf, &mylen, &b);
+ if (mylen < 0) {
+ ast_log(LOG_ERROR, "No start bit found in fsk data.\n");
+ free(obuf);
+ return -1;
+ }
+ buf += (olen - mylen);
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "fsk_serie failed\n");
+ free(obuf);
+ 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)) {
+ 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");
+ free(obuf);
+ 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';
+ /* If we get this far we're fine. */
+ if (cid->type == 0x80) {
+ /* 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 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 {
+ /* SDMF */
+ ast_copy_string(cid->number, cid->rawdata + 8, sizeof(cid->number));
+ }
+ /* Update flags */
+ cid->flags = 0;
+ 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;
+ }
+ free(obuf);
+ 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;
+ free(obuf);
+ return 0;
+}
+
+void callerid_free(struct callerid_state *cid)
+{
+ free(cid);
+}
+
+static int callerid_genmsg(char *msg, int size, const char *number, const char *name, int flags)
+{
+ time_t t;
+ struct tm tm;
+ char *ptr;
+ int res;
+ int i,x;
+ /* Get the time */
+ time(&t);
+ ast_localtime(&t, &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:
+ 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 Destructively parse instr for caller id information
+ * \return always returns 0, as the code always returns something.
+ * \note XXX '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)
+ * 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 if (ns) {
+ /* An opening quote was found but no closing quote was. The closing
+ * quote may actually be after the end of the bracketed number
+ */
+ if (strchr(le + 1, '\"')) {
+ *ns = '\0';
+ *name = ns + 1;
+ ast_trim_blanks(*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;
+ char *name;
+ 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";
+}
diff --git a/main/cdr.c b/main/cdr.c
new file mode 100644
index 000000000..39045c9ed
--- /dev/null
+++ b/main/cdr.c
@@ -0,0 +1,1499 @@
+/*
+ * 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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/logger.h"
+#include "asterisk/callerid.h"
+#include "asterisk/causes.h"
+#include "asterisk/options.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_LIST_ENTRY(ast_cdr_beitem) list;
+};
+
+static AST_LIST_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;
+
+
+/*! 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;
+
+ if (!name)
+ return -1;
+ if (!be) {
+ ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
+ return -1;
+ }
+
+ AST_LIST_LOCK(&be_list);
+ AST_LIST_TRAVERSE(&be_list, i, list) {
+ if (!strcasecmp(name, i->name))
+ break;
+ }
+ AST_LIST_UNLOCK(&be_list);
+
+ if (i) {
+ ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
+ 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_LIST_LOCK(&be_list);
+ AST_LIST_INSERT_HEAD(&be_list, i, list);
+ AST_LIST_UNLOCK(&be_list);
+
+ return 0;
+}
+
+/*! unregister a CDR driver */
+void ast_cdr_unregister(const char *name)
+{
+ struct ast_cdr_beitem *i = NULL;
+
+ AST_LIST_LOCK(&be_list);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) {
+ if (!strcasecmp(name, i->name)) {
+ AST_LIST_REMOVE_CURRENT(&be_list, list);
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s' CDR backend\n", name);
+ free(i);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&be_list);
+}
+
+int ast_cdr_isset_unanswered(void)
+{
+ return unanswered;
+}
+
+/*! 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 {
+ time_t t = tv.tv_sec;
+ if (t) {
+ struct tm tm;
+
+ ast_localtime(&t, &tm, NULL);
+ 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);
+ else if (!strcasecmp(name, "billsec"))
+ snprintf(workspace, workspacelen, "%ld", cdr->billsec);
+ 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_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+ continue;
+ 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(headp, 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, char *buf, size_t size, 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;
+
+ memset(buf, 0, size);
+
+ for (; cdr; cdr = recur ? cdr->next : NULL) {
+ if (++x > 1)
+ ast_build_string(&buf, &size, "\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_build_string(&buf, &size, "level %d: %s%c%s%c", x, var, delim, val, sep)) {
+ ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
+ break;
+ } else
+ total++;
+ } else
+ break;
+ }
+
+ for (i = 0; cdr_readonly_vars[i]; i++) {
+ workspace[0] = 0; /* null out the workspace, because the cdr_get_tv() won't write anything if time is NULL, so you get old vals */
+ ast_cdr_getvar(cdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
+ if (!tmp)
+ continue;
+
+ if (ast_build_string(&buf, &size, "level %d: %s%c%s%c", x, cdr_readonly_vars[i], delim, tmp, sep)) {
+ 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 (option_verbose > 1 && !ast_test_flag(cdr, AST_CDR_FLAG_POSTED) && !ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
+ ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' not posted\n", chan);
+ if (option_verbose > 1 && ast_tvzero(cdr->end))
+ ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks end\n", chan);
+ if (option_verbose > 1 && ast_tvzero(cdr->start))
+ ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks start\n", chan);
+
+ ast_cdr_free_vars(cdr, 0);
+ 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);
+ free(cdr);
+ cdr = next;
+ }
+}
+
+struct ast_cdr *ast_cdr_alloc(void)
+{
+ struct ast_cdr *x = ast_calloc(1, sizeof(struct ast_cdr));
+ 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 = NULL, *fromvarval = NULL;
+ 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_REMOVE_CURRENT(headpfrom, entries);
+ AST_LIST_INSERT_HEAD(headpto, variablesfrom, 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)) {
+ discard_from = 1;
+ if (lto) {
+ struct ast_cdr *llfrom = NULL;
+ /* 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;
+ struct ast_cdr *llfrom = NULL;
+ 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_ANSLOCKED))
+ continue;
+ if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+ continue;
+ 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) {
+ check_post(cdr);
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ if (cdr->disposition < AST_CDR_FAILED)
+ cdr->disposition = AST_CDR_FAILED;
+ }
+ }
+}
+
+void ast_cdr_noanswer(struct ast_cdr *cdr)
+{
+ char *chan;
+
+ while (cdr) {
+ 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 (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ 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_NO_ANSWER:
+ ast_cdr_noanswer(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) {
+ check_post(cdr);
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+ 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);
+ ast_copy_string(cdr->lastapp, S_OR(app, ""), sizeof(cdr->lastapp));
+ ast_copy_string(cdr->lastdata, S_OR(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;
+}
+
+/* Three routines were "fixed" via 10668, and later shown that
+ users were depending on this behavior. ast_cdr_end,
+ ast_cdr_setvar and ast_cdr_answer are the three routines.
+ While most of the other routines would not touch
+ LOCKED cdr's, these three routines were designed to
+ operate on locked CDR's as a matter of course.
+ I now appreciate how this plays with the ForkCDR app,
+ which forms these cdr chains in the first place.
+ cdr_end is pretty key: all cdrs created are closed
+ together. They only vary by start time. Arithmetically,
+ users can calculate the subintervals they wish to track. */
+
+void ast_cdr_end(struct ast_cdr *cdr)
+{
+ for ( ; cdr ; cdr = cdr->next) {
+ if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+ continue;
+ 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;
+ if (ast_tvzero(cdr->answer)) {
+ if (cdr->disposition == AST_CDR_ANSWERED) {
+ ast_log(LOG_WARNING, "CDR on channel '%s' has no answer time but is 'ANSWERED'\n", S_OR(cdr->channel, "<unknown>"));
+ cdr->disposition = AST_CDR_FAILED;
+ }
+ } else
+ cdr->billsec = 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;
+
+ 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));
+ }
+ }
+ 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) {
+ if (!unanswered && cdr->disposition < AST_CDR_ANSWERED && (ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->dstchannel))) {
+ /* For people, who don't want to see unanswered single-channel events */
+ ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
+ continue;
+ }
+
+ chan = S_OR(cdr->channel, "<unknown>");
+ check_post(cdr);
+ if (option_verbose > 1 && ast_tvzero(cdr->end))
+ ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks end\n", chan);
+ if (option_verbose > 1 && ast_tvzero(cdr->start))
+ ast_verbose(VERBOSE_PREFIX_2 "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_LIST_LOCK(&be_list);
+ AST_LIST_TRAVERSE(&be_list, i, list) {
+ i->be(cdr);
+ }
+ AST_LIST_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;
+ }
+ }
+}
+
+void ast_cdr_specialized_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
+{
+ struct ast_flags flags = { 0 };
+
+ if (_flags)
+ ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
+
+ /* Reset to initial state */
+ if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED)) { /* But do NOT lose the NoCDR() setting */
+ ast_clear_flag(cdr, AST_FLAGS_ALL);
+ ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
+ } else {
+ 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;
+ free(processeditem);
+ }
+
+ return NULL;
+}
+
+void ast_cdr_submit_batch(int shutdown)
+{
+ struct ast_cdr_batch_item *oldbatchitems = NULL;
+ pthread_attr_t attr;
+ 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) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "CDR single-threaded batch processing begins now\n");
+ do_batch_backend_process(oldbatchitems);
+ } else {
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ast_pthread_create_background(&batch_post_thread, &attr, 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 {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "CDR multi-threaded batch processing begins now\n");
+ }
+ pthread_attr_destroy(&attr);
+ }
+}
+
+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 */
+ 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) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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) */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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);
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "Processed %d scheduled CDR batches from the run queue\n", numevents);
+ }
+
+ return NULL;
+}
+
+static int handle_cli_status(int fd, int argc, char *argv[])
+{
+ struct ast_cdr_beitem *beitem=NULL;
+ int cnt=0;
+ long nextbatchtime=0;
+
+ if (argc > 2)
+ return RESULT_SHOWUSAGE;
+
+ ast_cli(fd, "CDR logging: %s\n", enabled ? "enabled" : "disabled");
+ ast_cli(fd, "CDR mode: %s\n", batchmode ? "batch" : "simple");
+ if (enabled) {
+ ast_cli(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(fd, "CDR safe shut down: %s\n", batchsafeshutdown ? "enabled" : "disabled");
+ ast_cli(fd, "CDR batch threading model: %s\n", batchscheduleronly ? "scheduler only" : "scheduler plus separate threads");
+ ast_cli(fd, "CDR current batch size: %d record%s\n", cnt, (cnt != 1) ? "s" : "");
+ ast_cli(fd, "CDR maximum batch size: %d record%s\n", batchsize, (batchsize != 1) ? "s" : "");
+ ast_cli(fd, "CDR maximum batch time: %d second%s\n", batchtime, (batchtime != 1) ? "s" : "");
+ ast_cli(fd, "CDR next scheduled batch processing time: %ld second%s\n", nextbatchtime, (nextbatchtime != 1) ? "s" : "");
+ }
+ AST_LIST_LOCK(&be_list);
+ AST_LIST_TRAVERSE(&be_list, beitem, list) {
+ ast_cli(fd, "CDR registered backend: %s\n", beitem->name);
+ }
+ AST_LIST_UNLOCK(&be_list);
+ }
+
+ return 0;
+}
+
+static int handle_cli_submit(int fd, int argc, char *argv[])
+{
+ if (argc > 2)
+ return RESULT_SHOWUSAGE;
+
+ submit_unscheduled_batch();
+ ast_cli(fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
+
+ return 0;
+}
+
+static struct ast_cli_entry cli_submit = {
+ { "cdr", "submit", NULL },
+ handle_cli_submit, "Posts all pending batched CDR data",
+ "Usage: cdr submit\n"
+ " Posts all pending batched CDR data to the configured CDR backend engine modules.\n"
+};
+
+static struct ast_cli_entry cli_status = {
+ { "cdr", "status", NULL },
+ handle_cli_status, "Display the CDR status",
+ "Usage: cdr status\n"
+ " Displays the Call Detail Record engine system status.\n"
+};
+
+static int do_reload(void)
+{
+ 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;
+
+ 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 */
+ AST_SCHED_DEL(sched, cdr_sched);
+
+ if ((config = ast_config_load("cdr.conf"))) {
+ 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 (cfg_size < 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 (cfg_time < 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);
+
+ 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();
+ 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();
+}
+
diff --git a/main/channel.c b/main/channel.c
new file mode 100644
index 000000000..88ede049e
--- /dev/null
+++ b/main/channel.c
@@ -0,0 +1,4876 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <math.h>
+
+#if defined(HAVE_DAHDI)
+#include <sys/ioctl.h>
+#include "asterisk/dahdi_compat.h"
+#endif
+
+#include "asterisk/pbx.h"
+#include "asterisk/frame.h"
+#include "asterisk/sched.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/audiohook.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/logger.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"
+
+/* 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
+
+/*! Prevent new channel allocation if shutting down. */
+static int shutting_down;
+
+static int uniqueint;
+
+unsigned long global_fin, global_fout;
+
+AST_THREADSTORAGE(state2str_threadbuf, state2str_threadbuf_init);
+#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
+
+struct chanlist {
+ const struct ast_channel_tech *tech;
+ AST_LIST_ENTRY(chanlist) list;
+};
+
+/*! the list of registered channel types */
+static AST_LIST_HEAD_NOLOCK_STATIC(backends, chanlist);
+
+/*! 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_LIST_HEAD_STATIC(channels, ast_channel);
+
+/*! map AST_CAUSE's to readable string representations */
+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;
+}
+
+static int show_channeltypes(int fd, int argc, char *argv[])
+{
+#define FORMAT "%-10.10s %-40.40s %-12.12s %-12.12s %-12.12s\n"
+ struct chanlist *cl;
+ int count_chan = 0;
+
+ ast_cli(fd, FORMAT, "Type", "Description", "Devicestate", "Indications", "Transfer");
+ ast_cli(fd, FORMAT, "----------", "-----------", "-----------", "-----------", "--------");
+ if (AST_LIST_LOCK(&channels)) {
+ ast_log(LOG_WARNING, "Unable to lock channel list\n");
+ return -1;
+ }
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ ast_cli(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_LIST_UNLOCK(&channels);
+ ast_cli(fd, "----------\n%d channel drivers registered.\n", count_chan);
+ return RESULT_SUCCESS;
+
+#undef FORMAT
+
+}
+
+static int show_channeltype_deprecated(int fd, int argc, char *argv[])
+{
+ struct chanlist *cl = NULL;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ if (AST_LIST_LOCK(&channels)) {
+ ast_log(LOG_WARNING, "Unable to lock channel list\n");
+ return RESULT_FAILURE;
+ }
+
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ if (!strncasecmp(cl->tech->type, argv[2], strlen(cl->tech->type))) {
+ break;
+ }
+ }
+
+
+ if (!cl) {
+ ast_cli(fd, "\n%s is not a registered channel driver.\n", argv[2]);
+ AST_LIST_UNLOCK(&channels);
+ return RESULT_FAILURE;
+ }
+
+ ast_cli(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_LIST_UNLOCK(&channels);
+ return RESULT_SUCCESS;
+}
+
+static int show_channeltype(int fd, int argc, char *argv[])
+{
+ struct chanlist *cl = NULL;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ if (AST_LIST_LOCK(&channels)) {
+ ast_log(LOG_WARNING, "Unable to lock channel list\n");
+ return RESULT_FAILURE;
+ }
+
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ if (!strncasecmp(cl->tech->type, argv[3], strlen(cl->tech->type))) {
+ break;
+ }
+ }
+
+
+ if (!cl) {
+ ast_cli(fd, "\n%s is not a registered channel driver.\n", argv[3]);
+ AST_LIST_UNLOCK(&channels);
+ return RESULT_FAILURE;
+ }
+
+ ast_cli(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_LIST_UNLOCK(&channels);
+ return RESULT_SUCCESS;
+}
+
+static char *complete_channeltypes_deprecated(const char *line, const char *word, int pos, int state)
+{
+ struct chanlist *cl;
+ int which = 0;
+ int wordlen;
+ char *ret = NULL;
+
+ if (pos != 2)
+ return NULL;
+
+ wordlen = strlen(word);
+
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ if (!strncasecmp(word, cl->tech->type, wordlen) && ++which > state) {
+ ret = strdup(cl->tech->type);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static char *complete_channeltypes(const char *line, const char *word, int pos, int state)
+{
+ struct chanlist *cl;
+ int which = 0;
+ int wordlen;
+ char *ret = NULL;
+
+ if (pos != 3)
+ return NULL;
+
+ wordlen = strlen(word);
+
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ if (!strncasecmp(word, cl->tech->type, wordlen) && ++which > state) {
+ ret = strdup(cl->tech->type);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static char show_channeltypes_usage[] =
+"Usage: core show channeltypes\n"
+" Lists available channel types registered in your Asterisk server.\n";
+
+static char show_channeltype_usage[] =
+"Usage: core show channeltype <name>\n"
+" Show details about the specified channel type, <name>.\n";
+
+static struct ast_cli_entry cli_show_channeltypes_deprecated = {
+ { "show", "channeltypes", NULL },
+ show_channeltypes, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_channeltype_deprecated = {
+ { "show", "channeltype", NULL },
+ show_channeltype_deprecated, NULL,
+ NULL, complete_channeltypes_deprecated };
+
+static struct ast_cli_entry cli_channel[] = {
+ { { "core", "show", "channeltypes", NULL },
+ show_channeltypes, "List available channel types",
+ show_channeltypes_usage, NULL, &cli_show_channeltypes_deprecated },
+
+ { { "core", "show", "channeltype", NULL },
+ show_channeltype, "Give more details on that channel type",
+ show_channeltype_usage, complete_channeltypes, &cli_show_channeltype_deprecated },
+};
+
+/*! \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 printf the string into a correctly sized mallocd buffer, and return the buffer */
+char *ast_safe_string_alloc(const char *fmt, ...)
+{
+ char *b2, buf[1];
+ int len;
+ va_list args;
+
+ va_start(args, fmt);
+ len = vsnprintf(buf, 1, fmt, args);
+ va_end(args);
+
+ if (!(b2 = ast_malloc(len + 1)))
+ return NULL;
+
+ va_start(args, fmt);
+ vsnprintf(b2, len + 1, fmt, args);
+ va_end(args);
+
+ return b2;
+}
+
+/*! \brief Initiate system shutdown */
+void ast_begin_shutdown(int hangup)
+{
+ struct ast_channel *c;
+ shutting_down = 1;
+ if (hangup) {
+ AST_LIST_LOCK(&channels);
+ AST_LIST_TRAVERSE(&channels, c, chan_list)
+ ast_softhangup(c, AST_SOFTHANGUP_SHUTDOWN);
+ AST_LIST_UNLOCK(&channels);
+ }
+}
+
+/*! \brief returns number of active/allocated channels */
+int ast_active_channels(void)
+{
+ struct ast_channel *c;
+ int cnt = 0;
+ AST_LIST_LOCK(&channels);
+ AST_LIST_TRAVERSE(&channels, c, chan_list)
+ cnt++;
+ AST_LIST_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 == 0) {
+ return (offset == 0) ? 0 : -1;
+ } else {
+ if (offset == 0) /* XXX why is this special ? */
+ return (1);
+ else {
+ 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_LIST_LOCK(&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_LIST_UNLOCK(&channels);
+ return -1;
+ }
+ }
+
+ if (!(chan = ast_calloc(1, sizeof(*chan)))) {
+ AST_LIST_UNLOCK(&channels);
+ return -1;
+ }
+ chan->tech = tech;
+ AST_LIST_INSERT_HEAD(&backends, chan, list);
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Registered handler for '%s' (%s)\n", chan->tech->type, chan->tech->description);
+
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Registered channel type '%s' (%s)\n", chan->tech->type,
+ chan->tech->description);
+
+ AST_LIST_UNLOCK(&channels);
+ return 0;
+}
+
+void ast_channel_unregister(const struct ast_channel_tech *tech)
+{
+ struct chanlist *chan;
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", tech->type);
+
+ AST_LIST_LOCK(&channels);
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&backends, chan, list) {
+ if (chan->tech == tech) {
+ AST_LIST_REMOVE_CURRENT(&backends, list);
+ free(chan);
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Unregistered channel type '%s'\n", tech->type);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ AST_LIST_UNLOCK(&channels);
+}
+
+const struct ast_channel_tech *ast_get_channel_tech(const char *name)
+{
+ struct chanlist *chanls;
+ const struct ast_channel_tech *ret = NULL;
+
+ if (AST_LIST_LOCK(&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_LIST_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 */
+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 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_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");
+ free(tmp);
+ return NULL;
+ }
+
+ if ((ast_string_field_init(tmp, 128))) {
+ sched_context_destroy(tmp->sched);
+ free(tmp);
+ return NULL;
+ }
+
+ /* Don't bother initializing the last two FD here, because they
+ will *always* be set just a few lines down (AST_TIMING_FD,
+ AST_ALERT_FD). */
+ for (x = 0; x < AST_MAX_FDS - 2; x++)
+ tmp->fds[x] = -1;
+
+#ifdef HAVE_DAHDI
+
+ tmp->timingfd = open(DAHDI_FILE_TIMER, O_RDWR);
+
+ if (tmp->timingfd > -1) {
+ /* Check if timing interface supports new
+ ping/pong scheme */
+ flags = 1;
+ if (!ioctl(tmp->timingfd, DAHDI_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");
+alertpipe_failed:
+#ifdef HAVE_DAHDI
+ if (tmp->timingfd > -1)
+ close(tmp->timingfd);
+#endif
+ sched_context_destroy(tmp->sched);
+ ast_string_field_free_memory(tmp);
+ free(tmp);
+ return NULL;
+ } else {
+ flags = fcntl(tmp->alertpipe[0], F_GETFL);
+ if (fcntl(tmp->alertpipe[0], F_SETFL, flags | O_NONBLOCK) < 0) {
+ ast_log(LOG_WARNING, "Channel allocation failed: Unable to set alertpipe nonblocking! (%d: %s)\n", errno, strerror(errno));
+ close(tmp->alertpipe[0]);
+ close(tmp->alertpipe[1]);
+ goto alertpipe_failed;
+ }
+ flags = fcntl(tmp->alertpipe[1], F_GETFL);
+ if (fcntl(tmp->alertpipe[1], F_SETFL, flags | O_NONBLOCK) < 0) {
+ ast_log(LOG_WARNING, "Channel allocation failed: Unable to set alertpipe nonblocking! (%d: %s)\n", errno, strerror(errno));
+ close(tmp->alertpipe[0]);
+ close(tmp->alertpipe[1]);
+ goto alertpipe_failed;
+ }
+ }
+ } else /* Make sure we've got it done right if they don't */
+ tmp->alertpipe[0] = tmp->alertpipe[1] = -1;
+
+ /* Always watch the alertpipe */
+ tmp->fds[AST_ALERT_FD] = tmp->alertpipe[0];
+ /* And timing pipe */
+ tmp->fds[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);
+
+ AST_LIST_HEAD_INIT_NOLOCK(&tmp->datastores);
+
+ ast_string_field_set(tmp, language, defaultlanguage);
+
+ tmp->tech = &null_tech;
+
+ AST_LIST_LOCK(&channels);
+ AST_LIST_INSERT_HEAD(&channels, tmp, chan_list);
+ AST_LIST_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"
+ "State: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Uniqueid: %s\r\n",
+ tmp->name, ast_state2str(state),
+ S_OR(cid_num, "<unknown>"),
+ S_OR(cid_name, "<unknown>"),
+ tmp->uniqueid);
+ }
+
+ return tmp;
+}
+
+/*! \brief Queue an outgoing media frame */
+static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int head)
+{
+ 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))) {
+ 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);
+ ast_assert(fin->frametype == AST_FRAME_VOICE);
+ } else {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Dropping voice to exceptionally long queue on %s\n", chan->name);
+ ast_frfree(f);
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ }
+
+ if (head) {
+ AST_LIST_INSERT_HEAD(&chan->readq, f, frame_list);
+ } else {
+ 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_DAHDI
+ } else if (chan->timingfd > -1) {
+ ioctl(chan->timingfd, DAHDI_TIMERPING, &blah);
+#endif
+ } else if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
+ pthread_kill(chan->blocker, SIGURG);
+ }
+
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
+int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
+{
+ return __ast_queue_frame(chan, fin, 0);
+}
+
+int ast_queue_frame_head(struct ast_channel *chan, struct ast_frame *fin)
+{
+ return __ast_queue_frame(chan, fin, 1);
+}
+
+/*! \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 < 200; retries++) {
+ int done;
+ /* Reset prev on each retry. See note below for the reason. */
+ prev = _prev;
+ AST_LIST_LOCK(&channels);
+ AST_LIST_TRAVERSE(&channels, c, chan_list) {
+ if (prev) { /* look for last item, first, before any evaluation */
+ if (c != prev) /* not this one */
+ continue;
+ /* found, prepare to return c->next */
+ if ((c = AST_LIST_NEXT(c, chan_list)) == NULL) break;
+ /*!\note
+ * We're done searching through the list for the previous item.
+ * Any item after this point, we want to evaluate for a match.
+ * If we didn't set prev to NULL here, then we would only
+ * return matches for the first matching item (since the above
+ * "if (c != prev)" would not permit any other potential
+ * matches to reach the additional matching logic, below).
+ * Instead, it would just iterate until it once again found the
+ * original match, then iterate down to the end of the list and
+ * quit.
+ */
+ prev = NULL;
+ }
+ if (name) { /* want match by name */
+ if ((!namelen && strcasecmp(c->name, 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) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Avoiding %s for channel '%p'\n", msg, c);
+ if (retries == 199) {
+ /* We are about to fail due to a deadlock, so report this
+ * while we still have the list lock.
+ */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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_LIST_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)
+ free(cid->cid_dnid);
+ if (cid->cid_num)
+ free(cid->cid_num);
+ if (cid->cid_name)
+ free(cid->cid_name);
+ if (cid->cid_ani)
+ free(cid->cid_ani);
+ if (cid->cid_rdnis)
+ free(cid->cid_rdnis);
+}
+
+/*! \brief Free a channel structure */
+void ast_channel_free(struct ast_channel *chan)
+{
+ int fd;
+ struct ast_var_t *vardata;
+ struct ast_frame *f;
+ struct varshead *headp;
+ struct ast_datastore *datastore = NULL;
+ char name[AST_CHANNEL_NAME], *dashptr;
+
+ headp=&chan->varshead;
+
+ AST_LIST_LOCK(&channels);
+ if (!AST_LIST_REMOVE(&channels, chan, chan_list)) {
+ AST_LIST_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
+ due to a reference retrieved from the channel list. */
+ ast_channel_lock(chan);
+ ast_channel_unlock(chan);
+
+ /* 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);
+
+ /* Lock and unlock the channel just to be sure nobody has it locked still
+ due to a reference that was stored in a datastore. (i.e. app_chanspy) */
+ 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);
+ free(chan->tech_pvt);
+ }
+
+ if (chan->sched)
+ sched_context_destroy(chan->sched);
+
+ ast_copy_string(name, chan->name, sizeof(name));
+ if ((dashptr = strrchr(name, '-'))) {
+ *dashptr = '\0';
+ }
+
+ /* 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);
+ /* 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);
+ while ((f = AST_LIST_REMOVE_HEAD(&chan->readq, frame_list)))
+ ast_frfree(f);
+
+ /* 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);
+
+ if (chan->cdr) {
+ ast_cdr_discard(chan->cdr);
+ chan->cdr = NULL;
+ }
+
+ ast_mutex_destroy(&chan->lock);
+
+ ast_string_field_free_memory(chan);
+ free(chan);
+ AST_LIST_UNLOCK(&channels);
+
+ ast_device_state_changed_literal(name);
+}
+
+struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, char *uid)
+{
+ struct ast_datastore *datastore = NULL;
+
+ /* Make sure we at least have type so we can identify this */
+ if (info == NULL) {
+ return NULL;
+ }
+
+ /* Allocate memory for datastore and clear it */
+ datastore = ast_calloc(1, sizeof(*datastore));
+ if (datastore == NULL) {
+ 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) {
+ free(datastore->uid);
+ datastore->uid = NULL;
+ }
+
+ /* Finally free memory used by ourselves */
+ 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->info->duplicate(datastore->data) : NULL;
+ 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)
+{
+ struct ast_datastore *datastore2 = NULL;
+ int res = -1;
+
+ /* Find our position and remove ourselves */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->datastores, datastore2, entry) {
+ if (datastore2 == datastore) {
+ AST_LIST_REMOVE_CURRENT(&chan->datastores, entry);
+ res = 0;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ return res;
+}
+
+struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, 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;
+}
+
+/*! \brief Softly hangup a channel, don't lock */
+int ast_softhangup_nolock(struct ast_channel *chan, int cause)
+{
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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;
+
+ /* 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 */
+ chan->generator->release(chan, chan->generatordata);
+ chan->generatordata = NULL;
+ chan->generator = 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);
+ ast_assert(ast_test_flag(chan, AST_FLAG_BLOCKING) == 0);
+ }
+ if (!ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Hanging up channel '%s'\n", chan->name);
+ if (chan->tech->hangup)
+ res = chan->tech->hangup(chan);
+ } else {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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"
+ "Cause: %d\r\n"
+ "Cause-txt: %s\r\n",
+ chan->name,
+ chan->uniqueid,
+ chan->hangupcause,
+ ast_cause2str(chan->hangupcause)
+ );
+
+ if (chan->cdr && !ast_test_flag(chan->cdr, AST_CDR_FLAG_BRIDGED) &&
+ !ast_test_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED) &&
+ (chan->cdr->disposition != AST_CDR_NULL || ast_test_flag(chan->cdr, AST_CDR_FLAG_DIALED))) {
+
+ ast_cdr_end(chan->cdr);
+ ast_cdr_detach(chan->cdr);
+ chan->cdr = NULL;
+ }
+
+ ast_channel_free(chan);
+
+ return res;
+}
+
+int ast_answer(struct ast_channel *chan)
+{
+ 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);
+ break;
+ case AST_STATE_UP:
+ break;
+ default:
+ break;
+ }
+ chan->visible_indication = 0;
+ ast_channel_unlock(chan);
+ return res;
+}
+
+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;
+ chan->fds[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) = NULL;
+ struct ast_channel *chan = (struct ast_channel *)data;
+
+ ast_channel_lock(chan);
+ tmp = chan->generatordata;
+ chan->generatordata = NULL;
+ if (chan->generator)
+ generate = chan->generator->generate;
+ ast_channel_unlock(chan);
+
+ if (!tmp || !generate)
+ return 0;
+
+ res = generate(chan, tmp, 0, ast_format_rate(chan->writeformat & AST_FORMAT_AUDIO_MASK) / 50);
+
+ chan->generatordata = tmp;
+
+ if (res) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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. */
+struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds,
+ int *exception, int *outfd, int *ms)
+{
+ struct timeval start = { 0 , 0 };
+ struct pollfd *pfds = NULL;
+ 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 = NULL;
+
+ if ((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) {
+ if (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;
+}
+
+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_DAHDI
+ if (c->timingfd > -1) {
+ if (!func) {
+ samples = 0;
+ data = 0;
+ }
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Scheduling timer at %d sample intervals\n", samples);
+ res = ioctl(c->timingfd, DAHDI_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;
+
+ 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_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:
+ case AST_CONTROL_SRCUPDATE:
+ /* 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) {
+ if (write(audiofd, f->data, f->datalen) < 0) {
+ ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
+ }
+ }
+ default:
+ /* Ignore */
+ break;
+ }
+ ast_frfree(f);
+ }
+ }
+
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+
+ return 0; /* Time is up */
+}
+
+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 (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples) = NULL;
+ int res;
+ int samples;
+
+ 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 */
+
+ if (f->subclass != chan->writeformat) {
+ float factor;
+ factor = ((float) ast_format_rate(chan->writeformat)) / ((float) ast_format_rate(f->subclass));
+ samples = (int) ( ((float) f->samples) * factor );
+ } else {
+ samples = f->samples;
+ }
+
+ if (chan->generator->generate) {
+ generate = chan->generator->generate;
+ }
+ /* This unlock is here based on two assumptions that hold true at this point in the
+ * code. 1) this function is only called from within __ast_read() and 2) all generators
+ * call ast_write() in their generate callback.
+ *
+ * The reason this is added is so that when ast_write is called, the lock that occurs
+ * there will not recursively lock the channel. Doing this will cause intended deadlock
+ * avoidance not to work in deeper functions
+ */
+ ast_channel_unlock(chan);
+ res = generate(chan, tmp, f->datalen, samples);
+ ast_channel_lock(chan);
+ 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 inline void queue_dtmf_readq(struct ast_channel *chan, struct ast_frame *f)
+{
+ struct ast_frame *fr = &chan->dtmff;
+
+ fr->frametype = AST_FRAME_DTMF_END;
+ fr->subclass = f->subclass;
+ fr->len = f->len;
+
+ /* The only time this function will be called is for a frame that just came
+ * out of the channel driver. So, we want to stick it on the tail of the
+ * readq. */
+
+ ast_queue_frame(chan, fr);
+}
+
+/*!
+ * \brief Determine whether or not we should ignore DTMF in the readq
+ */
+static inline int should_skip_dtmf(struct ast_channel *chan)
+{
+ if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF)) {
+ /* We're in the middle of emulating a digit, or DTMF has been
+ * explicitly deferred. Skip this digit, then. */
+ return 1;
+ }
+
+ if (!ast_tvzero(chan->dtmf_tv) &&
+ ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) < AST_MIN_DTMF_GAP) {
+ /* We're not in the middle of a digit, but it hasn't been long enough
+ * since the last digit, so we'll have to skip DTMF for now. */
+ return 1;
+ }
+
+ return 0;
+}
+
+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;
+
+ /* 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) {
+ int flags = fcntl(chan->alertpipe[0], F_GETFL);
+ /* For some odd reason, the alertpipe occasionally loses nonblocking status,
+ * which immediately causes a deadlock scenario. Detect and prevent this. */
+ if ((flags & O_NONBLOCK) == 0) {
+ ast_log(LOG_ERROR, "Alertpipe on channel %s lost O_NONBLOCK?!!\n", chan->name);
+ if (fcntl(chan->alertpipe[0], F_SETFL, flags | O_NONBLOCK) < 0) {
+ ast_log(LOG_WARNING, "Unable to set alertpipe nonblocking! (%d: %s)\n", errno, strerror(errno));
+ f = &ast_null_frame;
+ goto done;
+ }
+ }
+ if (read(chan->alertpipe[0], &blah, sizeof(blah)) < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
+ }
+ }
+
+#ifdef HAVE_DAHDI
+ 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, DAHDI_GETEVENT, &blah);
+ if (res)
+ blah = DAHDI_EVENT_TIMER_EXPIRED;
+
+ if (blah == DAHDI_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, DAHDI_TIMERPONG, &blah)) {
+ ast_log(LOG_WARNING, "Failed to pong timer on '%s': %s\n", chan->name, strerror(errno));
+ }
+ }
+ } else if (blah == DAHDI_EVENT_TIMER_EXPIRED) {
+ ioctl(chan->timingfd, DAHDI_TIMERACK, &blah);
+ if (chan->timingfunc) {
+ /* save a copy of func/data before unlocking the channel */
+ int (*func)(const void *) = chan->timingfunc;
+ void *data = chan->timingdata;
+ ast_channel_unlock(chan);
+ func(data);
+ } else {
+ blah = 0;
+ ioctl(chan->timingfd, DAHDI_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)) {
+ int skip_dtmf = should_skip_dtmf(chan);
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->readq, f, frame_list) {
+ /* We have to be picky about which frame we pull off of the readq because
+ * there are cases where we want to leave DTMF frames on the queue until
+ * some later time. */
+
+ if ( (f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END) && skip_dtmf) {
+ continue;
+ }
+
+ AST_LIST_REMOVE_CURRENT(&chan->readq, frame_list);
+ break;
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ if (!f) {
+ /* There were no acceptable frames on the readq. */
+ f = &ast_null_frame;
+ if (chan->alertpipe[0] > -1) {
+ int poke = 0;
+ /* Restore the state of the alertpipe since we aren't ready for any
+ * of the frames in the readq. */
+ if (write(chan->alertpipe[1], &poke, sizeof(poke)) != sizeof(poke)) {
+ ast_log(LOG_ERROR, "Failed to write to alertpipe: %s\n", strerror(errno));
+ }
+ }
+ }
+
+ /* 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)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Ignoring answer on an inbound call!\n");
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else if (prestate == AST_STATE_UP) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Dropping duplicate answer!\n");
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else {
+ /* Answer the CDR */
+ ast_setstate(chan, AST_STATE_UP);
+ /* removed a call to ast_cdr_answer(chan->cdr) from here. */
+ }
+ }
+ break;
+ case AST_FRAME_DTMF_END:
+ 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 deferred, or if DTMF emulation is forced. */
+ if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF) || ast_test_flag(chan, AST_FLAG_EMULATE_DTMF)) {
+ queue_dtmf_readq(chan, f);
+ 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 */
+ queue_dtmf_readq(chan, f);
+ 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;
+ 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_test_flag(chan, AST_FLAG_END_DTMF_ONLY)) {
+ 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);
+ if (f->len < AST_MIN_DTMF_DURATION) {
+ f->len = AST_MIN_DTMF_DURATION;
+ }
+ 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:
+ 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_FLAG_EMULATE_DTMF) ||
+ (!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:
+ /* 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)) {
+ struct timeval now = ast_tvnow();
+ if (!chan->emulate_dtmf_duration) {
+ ast_clear_flag(chan, AST_FLAG_EMULATE_DTMF);
+ chan->emulate_dtmf_digit = 0;
+ } else 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 is not one of the current native formats -- drop it on the floor */
+ char to[200];
+ 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_multiple(to, sizeof(to), chan->nativeformats));
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else if ((f->frametype == AST_FRAME_VOICE)) {
+ 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;
+
+ /* 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);
+ /* We no longer End the CDR here */
+ }
+
+ /* High bit prints debugging */
+ if (chan->fin & DEBUGCHAN_FLAG)
+ ast_frame_dump(chan->name, f, "<<");
+ chan->fin = FRAMECOUNT_INC(chan->fin);
+
+done:
+ ast_channel_unlock(chan);
+ return f;
+}
+
+int ast_internal_timing_enabled(struct ast_channel *chan)
+{
+ int ret = ast_opt_internal_timing && chan->timingfd > -1;
+ if (option_debug > 4)
+ ast_log(LOG_DEBUG, "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);
+}
+
+static int attribute_const is_visible_indication(enum ast_control_frame_type condition)
+{
+ /* Don't include a default case here so that we get compiler warnings
+ * when a new type is added. */
+
+ switch (condition) {
+ case AST_CONTROL_PROGRESS:
+ case AST_CONTROL_PROCEEDING:
+ case AST_CONTROL_VIDUPDATE:
+ case AST_CONTROL_SRCUPDATE:
+ case AST_CONTROL_RADIO_KEY:
+ case AST_CONTROL_RADIO_UNKEY:
+ case AST_CONTROL_OPTION:
+ case AST_CONTROL_WINK:
+ case AST_CONTROL_FLASH:
+ case AST_CONTROL_OFFHOOK:
+ case AST_CONTROL_TAKEOFFHOOK:
+ case AST_CONTROL_ANSWER:
+ case AST_CONTROL_HANGUP:
+ return 0;
+
+ case AST_CONTROL_CONGESTION:
+ case AST_CONTROL_BUSY:
+ case AST_CONTROL_RINGING:
+ case AST_CONTROL_RING:
+ case AST_CONTROL_HOLD:
+ case AST_CONTROL_UNHOLD:
+ return 1;
+ }
+
+ return 0;
+}
+
+int ast_indicate_data(struct ast_channel *chan, int _condition,
+ const void *data, size_t datalen)
+{
+ /* By using an enum, we'll get compiler warnings for values not handled
+ * in switch statements. */
+ enum ast_control_frame_type condition = _condition;
+ const struct tone_zone_sound *ts = NULL;
+ int res = -1;
+
+ ast_channel_lock(chan);
+
+ /* Don't bother if the channel is about to go away, anyway. */
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ if (chan->tech->indicate) {
+ /* See if the channel driver can handle this condition. */
+ res = chan->tech->indicate(chan, condition, data, datalen);
+ }
+
+ ast_channel_unlock(chan);
+
+ if (chan->tech->indicate && !res) {
+ /* The channel driver successfully handled this indication */
+ if (is_visible_indication(condition)) {
+ chan->visible_indication = condition;
+ }
+ return 0;
+ }
+
+ /* The channel driver does not support this indication, let's fake
+ * it by doing our own tone generation if applicable. */
+
+ /*!\note If we compare the enumeration type, which does not have any
+ * negative constants, the compiler may optimize this code away.
+ * Therefore, we must perform an integer comparison here. */
+ if (_condition < 0) {
+ /* Stop any tones that are playing */
+ ast_playtones_stop(chan);
+ return 0;
+ }
+
+ /* Handle conditions that we have tones for. */
+ 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;
+ case AST_CONTROL_PROGRESS:
+ case AST_CONTROL_PROCEEDING:
+ case AST_CONTROL_VIDUPDATE:
+ case AST_CONTROL_SRCUPDATE:
+ case AST_CONTROL_RADIO_KEY:
+ case AST_CONTROL_RADIO_UNKEY:
+ case AST_CONTROL_OPTION:
+ case AST_CONTROL_WINK:
+ case AST_CONTROL_FLASH:
+ case AST_CONTROL_OFFHOOK:
+ case AST_CONTROL_TAKEOFFHOOK:
+ case AST_CONTROL_ANSWER:
+ case AST_CONTROL_HANGUP:
+ case AST_CONTROL_RING:
+ case AST_CONTROL_HOLD:
+ case AST_CONTROL_UNHOLD:
+ /* Nothing left to do for these. */
+ res = 0;
+ break;
+ }
+
+ if (ts && ts->data[0]) {
+ /* We have a tone to play, yay. */
+ if (option_debug) {
+ ast_log(LOG_DEBUG, "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;
+ }
+
+ if (res) {
+ /* not handled */
+ ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, chan->name);
+ }
+
+ 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;
+ 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 */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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)
+{
+ if (chan->tech->send_digit_begin) {
+ ast_senddigit_begin(chan, digit);
+ ast_safe_sleep(chan, AST_DEFAULT_EMULATE_DTMF_DURATION);
+ }
+
+ return ast_senddigit_end(chan, digit, 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) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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;
+ int count = 0;
+ struct ast_frame *f = NULL, *f2 = NULL;
+
+ /*Deadlock avoidance*/
+ while(ast_channel_trylock(chan)) {
+ /*cannot goto done since the channel is not locked*/
+ if(count++ > 10) {
+ if(option_debug)
+ ast_log(LOG_DEBUG, "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;
+ }
+ 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;
+ }
+ 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:
+ 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 (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 we have no frame of audio, then we have to bail out */
+ 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;
+ char from[200], to[200];
+
+ /* 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_multiple(from, sizeof(from), native),
+ ast_getformatname_multiple(to, sizeof(to), 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);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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);
+}
+
+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);
+ if (oh->account)
+ ast_cdr_setaccount(chan, oh->account);
+ }
+ ast_set_callerid(chan, cid_num, cid_name, cid_num);
+
+ 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 AST_CONTROL_SRCUPDATE:
+ 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;
+
+ if (!cause)
+ cause = &foo;
+ *cause = AST_CAUSE_NOTDEFINED;
+
+ if (AST_LIST_LOCK(&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 %d) to %d\n", type, chan->tech->capabilities, format);
+ *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
+ AST_LIST_UNLOCK(&channels);
+ return NULL;
+ }
+ AST_LIST_UNLOCK(&channels);
+ if (!chan->tech->requester)
+ return NULL;
+
+ if (!(c = chan->tech->requester(type, capabilities | videoformat, 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_LIST_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->cdr)
+ ast_set_flag(chan->cdr, AST_CDR_FLAG_DIALED);
+ 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);
+}
+
+int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
+{
+ int src;
+ int dst;
+
+ if (chan->readformat == peer->writeformat && chan->writeformat == peer->readformat) {
+ /* Already compatible! Moving on ... */
+ return 0;
+ }
+
+ /* Set up translation from the chan to the peer */
+ src = chan->nativeformats;
+ dst = peer->nativeformats;
+ if (ast_translator_best_choice(&dst, &src) < 0) {
+ ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", chan->name, src, peer->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(chan, dst) < 0) {
+ ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", chan->name, dst);
+ return -1;
+ }
+ if (ast_set_write_format(peer, dst) < 0) {
+ ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", peer->name, dst);
+ return -1;
+ }
+
+ /* Set up translation from the peer to the chan */
+ src = peer->nativeformats;
+ dst = chan->nativeformats;
+ if (ast_translator_best_choice(&dst, &src) < 0) {
+ ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", peer->name, src, chan->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(peer, dst) < 0) {
+ ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", peer->name, dst);
+ return -1;
+ }
+ if (ast_set_write_format(chan, dst) < 0) {
+ ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", chan->name, dst);
+ return -1;
+ }
+ return 0;
+}
+
+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;
+ }
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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", "Oldname: %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);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Copying soft-transferable variable %s.\n", ast_var_name(newvar));
+ }
+ break;
+ case 2:
+ newvar = ast_var_assign(ast_var_full_name(current), ast_var_value(current));
+ if (newvar) {
+ AST_LIST_INSERT_TAIL(&child->varshead, newvar, entries);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Copying hard-transferable variable %s.\n", ast_var_name(newvar));
+ }
+ break;
+ default:
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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[100];
+ char orig[100];
+ char masqn[100];
+ char zombn[100];
+
+ if (option_debug > 3)
+ ast_log(LOG_DEBUG, "Actually Masquerading %s(%d) into the structure of %s(%d)\n",
+ clone->name, clone->_state, original->name, 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);
+
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "Got clone lock for masquerade on '%s' at %p\n", clone->name, &clone->lock);
+
+ /* 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", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", newn, masqn, clone->uniqueid);
+ manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %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;
+
+ if (write(original->alertpipe[1], &poke, sizeof(poke)) < 0) {
+ ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
+ }
+ }
+ }
+ }
+
+ /* 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", "Oldname: %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)
+ original->fds[x] = clone->fds[x];
+ }
+
+ ast_app_group_update(clone, original);
+ /* Move data stores over */
+ if (AST_LIST_FIRST(&clone->datastores)) {
+ struct ast_datastore *ds;
+ AST_LIST_TRAVERSE(&clone->datastores, ds, entry) {
+ if (ds->info->chan_fixup)
+ ds->info->chan_fixup(ds->data, clone, original);
+ }
+ 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 | AST_FLAG_OUTGOING);
+ 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 */
+ original->fds[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);
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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
+ *
+ * This is needed because the masquerade is swapping out in the internals
+ * of this channel, and the new channel private data needs to be made
+ * aware of the current visible indication (RINGING, CONGESTION, etc.)
+ */
+ 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)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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 {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Done Masquerading %s (%d)\n", original->name, original->_state);
+ return 0;
+}
+
+void ast_set_callerid(struct ast_channel *chan, const char *callerid, const char *calleridname, const char *ani)
+{
+ ast_channel_lock(chan);
+
+ if (callerid) {
+ if (chan->cid.cid_num)
+ free(chan->cid.cid_num);
+ chan->cid.cid_num = ast_strdup(callerid);
+ }
+ if (calleridname) {
+ if (chan->cid.cid_name)
+ free(chan->cid.cid_name);
+ chan->cid.cid_name = ast_strdup(calleridname);
+ }
+ if (ani) {
+ if (chan->cid.cid_ani)
+ free(chan->cid.cid_ani);
+ chan->cid.cid_ani = ast_strdup(ani);
+ }
+ manager_event(EVENT_FLAG_CALL, "Newcallerid",
+ "Channel: %s\r\n"
+ "CallerID: %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, "<Unknown>"),
+ S_OR(chan->cid.cid_name, "<Unknown>"),
+ 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)
+{
+ char name[AST_CHANNEL_NAME], *dashptr;
+ int oldstate = chan->_state;
+
+ if (oldstate == state)
+ return 0;
+
+ ast_copy_string(name, chan->name, sizeof(name));
+ if ((dashptr = strrchr(name, '-'))) {
+ *dashptr = '\0';
+ }
+
+ chan->_state = state;
+ ast_device_state_changed_literal(name);
+ /* setstate used to conditionally report Newchannel; this is no more */
+ manager_event(EVENT_FLAG_CALL,
+ "Newstate",
+ "Channel: %s\r\n"
+ "State: %s\r\n"
+ "CallerID: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Uniqueid: %s\r\n",
+ chan->name, ast_state2str(chan->_state),
+ S_OR(chan->cid.cid_num, "<unknown>"),
+ S_OR(chan->cid.cid_name, "<unknown>"),
+ 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", chan->language, "");
+ if (min) {
+ ast_say_number(chan, min, AST_DIGIT_ANY, chan->language, NULL);
+ ast_stream_and_wait(chan, "queue-minutes", chan->language, "");
+ }
+ if (sec) {
+ ast_say_number(chan, sec, AST_DIGIT_ANY, chan->language, NULL);
+ ast_stream_and_wait(chan, "queue-seconds", chan->language, "");
+ }
+ } else {
+ ast_stream_and_wait(chan, sound, chan->language, "");
+ }
+
+ 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);
+ if (jb_in_use)
+ ast_jb_empty_and_reset(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;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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:
+ case AST_CONTROL_SRCUPDATE:
+ ast_indicate_data(other, f->subclass, f->data, f->datalen);
+ if (jb_in_use) {
+ ast_jb_empty_and_reset(c0, c1);
+ }
+ break;
+ default:
+ *fo = f;
+ *rc = who;
+ bridge_exit = 1;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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);
+
+ /* Swap who gets priority */
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+ }
+ return res;
+}
+
+/*! \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;
+
+ /* \todo XXX here should check that cid_num is not NULL */
+ manager_event(EVENT_FLAG_CALL, "Link",
+ "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);
+
+ 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);
+
+ /* Before we enter in and bridge these two together tell them both the source of audio has changed */
+ ast_indicate(c0, AST_CONTROL_SRCUPDATE);
+ ast_indicate(c1, AST_CONTROL_SRCUPDATE);
+
+ 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;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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 && !ast_test_flag(&(config->features_callee),AST_FEATURE_REDIRECT) &&
+ !ast_test_flag(&(config->features_caller),AST_FEATURE_REDIRECT) &&
+ !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);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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);
+ /* \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);
+ return AST_BRIDGE_FAILED;
+ }
+ o0nativeformats = c0->nativeformats;
+ o1nativeformats = c1->nativeformats;
+ }
+
+ 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);
+
+ 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);
+
+ /* Now that we have broken the bridge the source will change yet again */
+ ast_indicate(c0, AST_CONTROL_SRCUPDATE);
+ ast_indicate(c1, AST_CONTROL_SRCUPDATE);
+
+ 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);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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);
+ 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);
+
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_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 - strlen(buf) - 1);
+ } else {
+ first=0;
+ }
+ snprintf(num, sizeof(num), "%u", i);
+ strncat(buf, num, buflen - strlen(buf) - 1);
+ }
+ }
+ 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");
+ free(state);
+ return NULL;
+ }
+
+ ast_activate_generator(chan, &silence_generator, state);
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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);
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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");
+
+ 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, const char *filename, int lineno, const char *func)
+{
+ int res = 0;
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "::::==== Unlocking AST channel %s\n", chan->name);
+
+ if (!chan) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "::::==== Unlocking non-existing channel \n");
+ return 0;
+ }
+#ifdef DEBUG_THREADS
+ res = __ast_pthread_mutex_unlock(filename, lineno, func, "(channel lock)", &chan->lock);
+#else
+ res = ast_mutex_unlock(&chan->lock);
+#endif
+
+ if (option_debug > 2) {
+#ifdef DEBUG_THREADS
+ int count = 0;
+ if ((count = chan->lock.reentrancy))
+ ast_log(LOG_DEBUG, ":::=== Still have %d locks (recursive)\n", count);
+#endif
+ if (!res)
+ if (option_debug)
+ ast_log(LOG_DEBUG, "::::==== Channel %s was unlocked\n", chan->name);
+ if (res == EINVAL) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "::::==== Channel %s had no lock by this thread. Failed unlocking\n", chan->name);
+ }
+ }
+ if (res == EPERM) {
+ /* We had no lock, so okay any way*/
+ if (option_debug > 3)
+ ast_log(LOG_DEBUG, "::::==== 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, const char *filename, int lineno, const char *func)
+{
+ int res;
+
+ if (option_debug > 3)
+ ast_log(LOG_DEBUG, "====:::: Locking AST channel %s\n", chan->name);
+
+#ifdef DEBUG_THREADS
+ res = __ast_pthread_mutex_lock(filename, lineno, func, "(channel lock)", &chan->lock);
+#else
+ res = ast_mutex_lock(&chan->lock);
+#endif
+
+ if (option_debug > 3) {
+#ifdef DEBUG_THREADS
+ int count = 0;
+ if ((count = chan->lock.reentrancy))
+ ast_log(LOG_DEBUG, ":::=== Now have %d locks (recursive)\n", count);
+#endif
+ if (!res)
+ ast_log(LOG_DEBUG, "::::==== Channel %s was locked\n", chan->name);
+ if (res == EDEADLK) {
+ /* We had no lock, so okey any way */
+ if (option_debug > 3)
+ ast_log(LOG_DEBUG, "::::==== Channel %s was not locked by us. Lock would cause deadlock.\n", chan->name);
+ }
+ if (res == EINVAL) {
+ if (option_debug > 3)
+ ast_log(LOG_DEBUG, "::::==== 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, const char *filename, int lineno, const char *func)
+{
+ int res;
+
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "====:::: Trying to lock AST channel %s\n", chan->name);
+#ifdef DEBUG_THREADS
+ res = __ast_pthread_mutex_trylock(filename, lineno, func, "(channel lock)", &chan->lock);
+#else
+ res = ast_mutex_trylock(&chan->lock);
+#endif
+
+ if (option_debug > 2) {
+#ifdef DEBUG_THREADS
+ int count = 0;
+ if ((count = chan->lock.reentrancy))
+ ast_log(LOG_DEBUG, ":::=== Now have %d locks (recursive)\n", count);
+#endif
+ if (!res)
+ ast_log(LOG_DEBUG, "::::==== Channel %s was locked\n", chan->name);
+ if (res == EBUSY) {
+ /* We failed to lock */
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "::::==== Channel %s failed to lock. Not waiting around...\n", chan->name);
+ }
+ if (res == EDEADLK) {
+ /* We had no lock, so okey any way*/
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "::::==== Channel %s was not locked. Lock would cause deadlock.\n", chan->name);
+ }
+ if (res == EINVAL && option_debug > 2)
+ ast_log(LOG_DEBUG, "::::==== 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/main/chanvars.c b/main/chanvars.c
new file mode 100644
index 000000000..7e617c1fb
--- /dev/null
+++ b/main/chanvars.c
@@ -0,0 +1,87 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "asterisk/chanvars.h"
+#include "asterisk/logger.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)
+ 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/main/cli.c b/main/cli.c
new file mode 100644
index 000000000..ef3ce6d19
--- /dev/null
+++ b/main/cli.c
@@ -0,0 +1,2037 @@
+/*
+ * 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 <unistd.h>
+#include <stdlib.h>
+#include <sys/signal.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/options.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"
+
+extern unsigned long global_fin, global_fout;
+
+AST_THREADSTORAGE(ast_cli_buf, ast_cli_buf_init);
+
+/*! \brief Initial buffer size for resulting strings in ast_cli() */
+#define AST_CLI_INITLEN 256
+
+void ast_cli(int fd, char *fmt, ...)
+{
+ int res;
+ struct ast_dynamic_str *buf;
+ va_list ap;
+
+ if (!(buf = ast_dynamic_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN)))
+ return;
+
+ va_start(ap, fmt);
+ res = ast_dynamic_str_thread_set_va(&buf, 0, &ast_cli_buf, fmt, ap);
+ va_end(ap);
+
+ if (res != AST_DYNSTR_BUILD_FAILED)
+ ast_carefulwrite(fd, buf->str, strlen(buf->str), 100);
+}
+
+static AST_LIST_HEAD_STATIC(helpers, ast_cli_entry);
+
+static char load_help[] =
+"Usage: module load <module name>\n"
+" Loads the specified module into Asterisk.\n";
+
+static char unload_help[] =
+"Usage: module unload [-f|-h] <module name>\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";
+
+static char help_help[] =
+"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";
+
+static char chanlist_help[] =
+"Usage: core show channels [concise|verbose]\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.\n";
+
+static char reload_help[] =
+"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";
+
+static char verbose_help[] =
+"Usage: core set verbose <level>\n"
+" Sets level of verbose messages to be displayed. 0 means\n"
+" no messages should be displayed. Equivalent to -v[v[v...]]\n"
+" on startup\n";
+
+static char debug_help[] =
+"Usage: core set debug <level> [filename]\n"
+" Sets level of core debug messages to be displayed. 0 means\n"
+" no messages should be displayed. Equivalent to -d[d[d...]]\n"
+" on startup. If filename is specified, debugging will be\n"
+" limited to just that file.\n";
+
+static char nodebug_help[] =
+"Usage: core set debug off\n"
+" Turns off core debug messages.\n";
+
+static char logger_mute_help[] =
+"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";
+
+static char softhangup_help[] =
+"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";
+
+static char group_show_channels_help[] =
+"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";
+
+static int handle_load_deprecated(int fd, int argc, char *argv[])
+{
+ if (argc != 2)
+ return RESULT_SHOWUSAGE;
+ if (ast_load_resource(argv[1])) {
+ ast_cli(fd, "Unable to load module %s\n", argv[1]);
+ return RESULT_FAILURE;
+ }
+ return RESULT_SUCCESS;
+}
+
+static int handle_load(int fd, int argc, char *argv[])
+{
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ if (ast_load_resource(argv[2])) {
+ ast_cli(fd, "Unable to load module %s\n", argv[2]);
+ return RESULT_FAILURE;
+ }
+ return RESULT_SUCCESS;
+}
+
+static int handle_reload_deprecated(int fd, int argc, char *argv[])
+{
+ int x;
+ int res;
+ if (argc < 1)
+ return RESULT_SHOWUSAGE;
+ if (argc > 1) {
+ for (x = 1; x < argc; x++) {
+ res = ast_module_reload(argv[x]);
+ switch(res) {
+ case 0:
+ ast_cli(fd, "No such module '%s'\n", argv[x]);
+ break;
+ case 1:
+ ast_cli(fd, "Module '%s' does not support reload\n", argv[x]);
+ break;
+ }
+ }
+ } else
+ ast_module_reload(NULL);
+ return RESULT_SUCCESS;
+}
+
+static int handle_reload(int fd, int argc, char *argv[])
+{
+ int x;
+ int res;
+ if (argc < 2)
+ return RESULT_SHOWUSAGE;
+ if (argc > 2) {
+ for (x = 2; x < argc; x++) {
+ res = ast_module_reload(argv[x]);
+ switch(res) {
+ case 0:
+ ast_cli(fd, "No such module '%s'\n", argv[x]);
+ break;
+ case 1:
+ ast_cli(fd, "Module '%s' does not support reload\n", argv[x]);
+ break;
+ }
+ }
+ } else
+ ast_module_reload(NULL);
+ return RESULT_SUCCESS;
+}
+
+static int handle_set_verbose_deprecated(int fd, int argc, char *argv[])
+{
+ int val = 0;
+ int oldval = option_verbose;
+
+ /* "set verbose [atleast] N" */
+ if (argc == 3)
+ option_verbose = atoi(argv[2]);
+ else if (argc == 4) {
+ if (strcasecmp(argv[2], "atleast"))
+ return RESULT_SHOWUSAGE;
+ val = atoi(argv[3]);
+ if (val > option_verbose)
+ option_verbose = val;
+ } else
+ return RESULT_SHOWUSAGE;
+
+ if (oldval != option_verbose && option_verbose > 0)
+ ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose);
+ else if (oldval > 0 && option_verbose > 0)
+ ast_cli(fd, "Verbosity is at least %d\n", option_verbose);
+ else if (oldval > 0 && option_verbose == 0)
+ ast_cli(fd, "Verbosity is now OFF\n");
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_verbose(int fd, int argc, char *argv[])
+{
+ int oldval = option_verbose;
+ int newlevel;
+ int atleast = 0;
+
+ if ((argc < 4) || (argc > 5))
+ return RESULT_SHOWUSAGE;
+
+ if (!strcasecmp(argv[3], "atleast"))
+ atleast = 1;
+
+ if (!atleast) {
+ if (argc > 4)
+ return RESULT_SHOWUSAGE;
+
+ option_verbose = atoi(argv[3]);
+ } else {
+ if (argc < 5)
+ return RESULT_SHOWUSAGE;
+
+ newlevel = atoi(argv[4]);
+ if (newlevel > option_verbose)
+ option_verbose = newlevel;
+ }
+ if (oldval > 0 && option_verbose == 0)
+ ast_cli(fd, "Verbosity is now OFF\n");
+ else if (option_verbose > 0) {
+ if (oldval == option_verbose)
+ ast_cli(fd, "Verbosity is at least %d\n", option_verbose);
+ else
+ ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose);
+ }
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_set_debug_deprecated(int fd, int argc, char *argv[])
+{
+ int val = 0;
+ int oldval = option_debug;
+
+ /* "set debug [atleast] N" */
+ if (argc == 3)
+ option_debug = atoi(argv[2]);
+ else if (argc == 4) {
+ if (strcasecmp(argv[2], "atleast"))
+ return RESULT_SHOWUSAGE;
+ val = atoi(argv[3]);
+ if (val > option_debug)
+ option_debug = val;
+ } else
+ return RESULT_SHOWUSAGE;
+
+ if (oldval != option_debug && option_debug > 0)
+ ast_cli(fd, "Core debug was %d and is now %d\n", oldval, option_debug);
+ else if (oldval > 0 && option_debug > 0)
+ ast_cli(fd, "Core debug is at least %d\n", option_debug);
+ else if (oldval > 0 && option_debug == 0)
+ ast_cli(fd, "Core debug is now OFF\n");
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_set_debug(int fd, int argc, char *argv[])
+{
+ int oldval = option_debug;
+ int newlevel;
+ int atleast = 0;
+ char *filename = '\0';
+
+ /* 'core set debug <level>'
+ * 'core set debug <level> <fn>'
+ * 'core set debug atleast <level>'
+ * 'core set debug atleast <level> <fn>'
+ */
+ if ((argc < 4) || (argc > 6))
+ return RESULT_SHOWUSAGE;
+
+ if (!strcasecmp(argv[3], "atleast"))
+ atleast = 1;
+
+ if (!atleast) {
+ if (argc > 5)
+ return RESULT_SHOWUSAGE;
+
+ if (sscanf(argv[3], "%d", &newlevel) != 1)
+ return RESULT_SHOWUSAGE;
+
+ if (argc == 4) {
+ debug_filename[0] = '\0';
+ } else {
+ filename = argv[4];
+ ast_copy_string(debug_filename, filename, sizeof(debug_filename));
+ }
+
+ option_debug = newlevel;
+ } else {
+ if (argc < 5 || argc > 6)
+ return RESULT_SHOWUSAGE;
+
+ if (sscanf(argv[4], "%d", &newlevel) != 1)
+ return RESULT_SHOWUSAGE;
+
+ if (argc == 5) {
+ debug_filename[0] = '\0';
+ } else {
+ filename = argv[5];
+ ast_copy_string(debug_filename, filename, sizeof(debug_filename));
+ }
+
+ if (newlevel > option_debug)
+ option_debug = newlevel;
+ }
+
+ if (oldval > 0 && option_debug == 0)
+ ast_cli(fd, "Core debug is now OFF\n");
+ else if (option_debug > 0) {
+ if (filename) {
+ if (oldval == option_debug)
+ ast_cli(fd, "Core debug is at least %d, file '%s'\n", option_debug, filename);
+ else
+ ast_cli(fd, "Core debug was %d and is now %d, file '%s'\n", oldval, option_debug, filename);
+ } else {
+ if (oldval == option_debug)
+ ast_cli(fd, "Core debug is at least %d\n", option_debug);
+ else
+ ast_cli(fd, "Core debug was %d and is now %d\n", oldval, option_debug);
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_nodebug(int fd, int argc, char *argv[])
+{
+ int oldval = option_debug;
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ option_debug = 0;
+ debug_filename[0] = '\0';
+
+ if (oldval > 0)
+ ast_cli(fd, "Core debug is now OFF\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_debuglevel_deprecated(int fd, int argc, char *argv[])
+{
+ int newlevel;
+ char *filename = "<any>";
+ if ((argc < 3) || (argc > 4))
+ return RESULT_SHOWUSAGE;
+ if (sscanf(argv[2], "%d", &newlevel) != 1)
+ return RESULT_SHOWUSAGE;
+ option_debug = newlevel;
+ if (argc == 4) {
+ filename = argv[3];
+ ast_copy_string(debug_filename, filename, sizeof(debug_filename));
+ } else {
+ debug_filename[0] = '\0';
+ }
+ ast_cli(fd, "Debugging level set to %d, file '%s'\n", newlevel, filename);
+ return RESULT_SUCCESS;
+}
+
+static int handle_logger_mute(int fd, int argc, char *argv[])
+{
+ if (argc < 2 || argc > 3)
+ return RESULT_SHOWUSAGE;
+ if (argc == 3 && !strcasecmp(argv[2], "silent"))
+ ast_console_toggle_mute(fd, 1);
+ else
+ ast_console_toggle_mute(fd, 0);
+ return RESULT_SUCCESS;
+}
+
+static int handle_unload_deprecated(int fd, int argc, char *argv[])
+{
+ int x;
+ int force = AST_FORCE_SOFT;
+ if (argc < 2)
+ return RESULT_SHOWUSAGE;
+ for (x = 1; x < argc; x++) {
+ if (argv[x][0] == '-') {
+ switch(argv[x][1]) {
+ case 'f':
+ force = AST_FORCE_FIRM;
+ break;
+ case 'h':
+ force = AST_FORCE_HARD;
+ break;
+ default:
+ return RESULT_SHOWUSAGE;
+ }
+ } else if (x != argc - 1)
+ return RESULT_SHOWUSAGE;
+ else if (ast_unload_resource(argv[x], force)) {
+ ast_cli(fd, "Unable to unload resource %s\n", argv[x]);
+ return RESULT_FAILURE;
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+static int handle_unload(int fd, int argc, char *argv[])
+{
+ int x;
+ int force = AST_FORCE_SOFT;
+ if (argc < 3)
+ return RESULT_SHOWUSAGE;
+ for (x = 2; x < argc; x++) {
+ if (argv[x][0] == '-') {
+ switch(argv[x][1]) {
+ case 'f':
+ force = AST_FORCE_FIRM;
+ break;
+ case 'h':
+ force = AST_FORCE_HARD;
+ break;
+ default:
+ return RESULT_SHOWUSAGE;
+ }
+ } else if (x != argc - 1)
+ return RESULT_SHOWUSAGE;
+ else if (ast_unload_resource(argv[x], force)) {
+ ast_cli(fd, "Unable to unload resource %s\n", argv[x]);
+ return RESULT_FAILURE;
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+#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 char modlist_help[] =
+"Usage: module show [like <keyword>]\n"
+" Shows Asterisk modules currently in use, and usage statistics.\n";
+
+static char uptime_help[] =
+"Usage: core show uptime [seconds]\n"
+" Shows Asterisk uptime information.\n"
+" The seconds word returns the uptime in seconds only.\n";
+
+static void print_uptimestr(int fd, time_t timeval, const char *prefix, int printsec)
+{
+ int x; /* the main part - years, weeks, etc. */
+ char timestr[256]="", *s = timestr;
+ size_t maxbytes = sizeof(timestr);
+
+#define SECOND (1)
+#define MINUTE (SECOND*60)
+#define HOUR (MINUTE*60)
+#define DAY (HOUR*24)
+#define WEEK (DAY*7)
+#define YEAR (DAY*365)
+#define ESS(x) ((x == 1) ? "" : "s") /* plural suffix */
+#define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
+ if (timeval < 0) /* invalid, nothing to show */
+ return;
+ if (printsec) { /* plain seconds output */
+ ast_build_string(&s, &maxbytes, "%lu", (u_long)timeval);
+ timeval = 0; /* bypass the other cases */
+ }
+ if (timeval > YEAR) {
+ x = (timeval / YEAR);
+ timeval -= (x * YEAR);
+ ast_build_string(&s, &maxbytes, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval));
+ }
+ if (timeval > WEEK) {
+ x = (timeval / WEEK);
+ timeval -= (x * WEEK);
+ ast_build_string(&s, &maxbytes, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval));
+ }
+ if (timeval > DAY) {
+ x = (timeval / DAY);
+ timeval -= (x * DAY);
+ ast_build_string(&s, &maxbytes, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval));
+ }
+ if (timeval > HOUR) {
+ x = (timeval / HOUR);
+ timeval -= (x * HOUR);
+ ast_build_string(&s, &maxbytes, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval));
+ }
+ if (timeval > MINUTE) {
+ x = (timeval / MINUTE);
+ timeval -= (x * MINUTE);
+ ast_build_string(&s, &maxbytes, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval));
+ }
+ x = timeval;
+ if (x > 0)
+ ast_build_string(&s, &maxbytes, "%d second%s ", x, ESS(x));
+ if (timestr[0] != '\0')
+ ast_cli(fd, "%s: %s\n", prefix, timestr);
+}
+
+static int handle_showuptime_deprecated(int fd, int argc, char *argv[])
+{
+ /* 'show uptime [seconds]' */
+ time_t curtime = time(NULL);
+ int printsec = (argc == 3 && !strcasecmp(argv[2],"seconds"));
+
+ if (argc != 2 && !printsec)
+ return RESULT_SHOWUSAGE;
+ if (ast_startuptime)
+ print_uptimestr(fd, curtime - ast_startuptime, "System uptime", printsec);
+ if (ast_lastreloadtime)
+ print_uptimestr(fd, curtime - ast_lastreloadtime, "Last reload", printsec);
+ return RESULT_SUCCESS;
+}
+
+static int handle_showuptime(int fd, int argc, char *argv[])
+{
+ /* 'core show uptime [seconds]' */
+ time_t curtime = time(NULL);
+ int printsec = (argc == 4 && !strcasecmp(argv[3],"seconds"));
+
+ if (argc != 3 && !printsec)
+ return RESULT_SHOWUSAGE;
+ if (ast_startuptime)
+ print_uptimestr(fd, curtime - ast_startuptime, "System uptime", printsec);
+ if (ast_lastreloadtime)
+ print_uptimestr(fd, curtime - ast_lastreloadtime, "Last reload", printsec);
+ return RESULT_SUCCESS;
+}
+
+static int handle_modlist(int fd, int argc, char *argv[])
+{
+ char *like = "";
+ if (argc == 3)
+ return RESULT_SHOWUSAGE;
+ else if (argc >= 4) {
+ if (strcmp(argv[2],"like"))
+ return RESULT_SHOWUSAGE;
+ like = argv[3];
+ }
+
+ ast_mutex_lock(&climodentrylock);
+ climodentryfd = fd; /* global, protected by climodentrylock */
+ ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
+ ast_cli(fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
+ climodentryfd = -1;
+ ast_mutex_unlock(&climodentrylock);
+ return RESULT_SUCCESS;
+}
+#undef MODLIST_FORMAT
+#undef MODLIST_FORMAT2
+
+#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\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"
+
+static int handle_chanlist_deprecated(int fd, int argc, char *argv[])
+{
+ struct ast_channel *c = NULL;
+ char durbuf[10] = "-";
+ char locbuf[40];
+ char appdata[40];
+ int duration;
+ int durh, durm, durs;
+ int numchans = 0, concise = 0, verbose = 0;
+
+ concise = (argc == 3 && (!strcasecmp(argv[2],"concise")));
+ verbose = (argc == 3 && (!strcasecmp(argv[2],"verbose")));
+
+ if (argc < 2 || argc > 3 || (argc == 3 && !concise && !verbose))
+ return RESULT_SHOWUSAGE;
+
+ 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);
+ if ((concise || verbose) && c->cdr && !ast_tvzero(c->cdr->start)) {
+ duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
+ if (verbose) {
+ durh = duration / 3600;
+ durm = (duration % 3600) / 60;
+ durs = duration % 60;
+ snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
+ } else {
+ snprintf(durbuf, sizeof(durbuf), "%d", duration);
+ }
+ } else {
+ durbuf[0] = '\0';
+ }
+ 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)");
+ } 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 {
+ if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten))
+ snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
+ else
+ strcpy(locbuf, "(None)");
+ if (c->appl)
+ snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, c->data ? c->data : "");
+ else
+ strcpy(appdata, "(None)");
+ 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()));
+ }
+ return RESULT_SUCCESS;
+}
+
+static int handle_chanlist(int fd, int argc, char *argv[])
+{
+ struct ast_channel *c = NULL;
+ char durbuf[10] = "-";
+ char locbuf[40];
+ char appdata[40];
+ int duration;
+ int durh, durm, durs;
+ int numchans = 0, concise = 0, verbose = 0;
+
+ concise = (argc == 4 && (!strcasecmp(argv[3],"concise")));
+ verbose = (argc == 4 && (!strcasecmp(argv[3],"verbose")));
+
+ if (argc < 3 || argc > 4 || (argc == 4 && !concise && !verbose))
+ return RESULT_SHOWUSAGE;
+
+ 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);
+ if ((concise || verbose) && c->cdr && !ast_tvzero(c->cdr->start)) {
+ duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
+ if (verbose) {
+ durh = duration / 3600;
+ durm = (duration % 3600) / 60;
+ durs = duration % 60;
+ snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
+ } else {
+ snprintf(durbuf, sizeof(durbuf), "%d", duration);
+ }
+ } else {
+ durbuf[0] = '\0';
+ }
+ 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)");
+ } 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 {
+ if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten))
+ snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
+ else
+ strcpy(locbuf, "(None)");
+ if (c->appl)
+ snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, c->data ? c->data : "");
+ else
+ strcpy(appdata, "(None)");
+ 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()));
+ }
+ return RESULT_SUCCESS;
+}
+
+#undef FORMAT_STRING
+#undef FORMAT_STRING2
+#undef CONCISE_FORMAT_STRING
+#undef VERBOSE_FORMAT_STRING
+#undef VERBOSE_FORMAT_STRING2
+
+static char showchan_help[] =
+"Usage: core show channel <channel>\n"
+" Shows lots of information about the specified channel.\n";
+
+static char debugchan_help[] =
+"Usage: core set debug channel <channel> [off]\n"
+" Enables/disables debugging on a specific channel.\n";
+
+static char commandcomplete_help[] =
+"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";
+
+static char commandnummatches_help[] =
+"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";
+
+static char commandmatchesarray_help[] =
+"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";
+
+static int handle_softhangup(int fd, int argc, char *argv[])
+{
+ struct ast_channel *c=NULL;
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ c = ast_get_channel_by_name_locked(argv[2]);
+ if (c) {
+ ast_cli(fd, "Requested Hangup on channel '%s'\n", c->name);
+ ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
+ ast_channel_unlock(c);
+ } else
+ ast_cli(fd, "%s is not a known channel\n", argv[2]);
+ return RESULT_SUCCESS;
+}
+
+static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
+
+static int handle_commandmatchesarray(int fd, int argc, char *argv[])
+{
+ char *buf, *obuf;
+ int buflen = 2048;
+ int len = 0;
+ char **matches;
+ int x, matchlen;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ if (!(buf = ast_malloc(buflen)))
+ return RESULT_FAILURE;
+ buf[len] = '\0';
+ matches = ast_cli_completion_matches(argv[2], 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 */
+ free(obuf);
+ }
+ if (buf)
+ len += sprintf( buf + len, "%s ", matches[x]);
+ free(matches[x]);
+ matches[x] = NULL;
+ }
+ free(matches);
+ }
+
+ if (buf) {
+ ast_cli(fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
+ free(buf);
+ } else
+ ast_cli(fd, "NULL\n");
+
+ return RESULT_SUCCESS;
+}
+
+
+
+static int handle_commandnummatches(int fd, int argc, char *argv[])
+{
+ int matches = 0;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ matches = ast_cli_generatornummatches(argv[2], argv[3]);
+
+ ast_cli(fd, "%d", matches);
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_commandcomplete(int fd, int argc, char *argv[])
+{
+ char *buf;
+
+ if (argc != 5)
+ return RESULT_SHOWUSAGE;
+ buf = __ast_cli_generator(argv[2], argv[3], atoi(argv[4]), 0);
+ if (buf) {
+ ast_cli(fd, "%s", buf);
+ free(buf);
+ } else
+ ast_cli(fd, "NULL\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_debugchan_deprecated(int fd, int argc, char *argv[])
+{
+ struct ast_channel *c=NULL;
+ int is_all;
+
+ /* 'debug channel {all|chan_id}' */
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ is_all = !strcasecmp("all", argv[2]);
+ if (is_all) {
+ global_fin |= DEBUGCHAN_FLAG;
+ global_fout |= DEBUGCHAN_FLAG;
+ c = ast_channel_walk_locked(NULL);
+ } else {
+ c = ast_get_channel_by_name_locked(argv[2]);
+ if (c == NULL)
+ ast_cli(fd, "No such channel %s\n", argv[2]);
+ }
+ while (c) {
+ if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
+ c->fin |= DEBUGCHAN_FLAG;
+ c->fout |= DEBUGCHAN_FLAG;
+ ast_cli(fd, "Debugging enabled on channel %s\n", c->name);
+ }
+ ast_channel_unlock(c);
+ if (!is_all)
+ break;
+ c = ast_channel_walk_locked(c);
+ }
+ ast_cli(fd, "Debugging on new channels is enabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_core_set_debug_channel(int fd, int argc, char *argv[])
+{
+ struct ast_channel *c = NULL;
+ int is_all, is_off = 0;
+
+ /* 'core set debug channel {all|chan_id}' */
+ if (argc == 6 && strcmp(argv[5], "off") == 0)
+ is_off = 1;
+ else if (argc != 5)
+ return RESULT_SHOWUSAGE;
+
+ is_all = !strcasecmp("all", argv[4]);
+ 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(argv[4]);
+ if (c == NULL)
+ ast_cli(fd, "No such channel %s\n", argv[4]);
+ }
+ 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(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(fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled");
+ return RESULT_SUCCESS;
+}
+
+static int handle_nodebugchan_deprecated(int fd, int argc, char *argv[])
+{
+ struct ast_channel *c=NULL;
+ int is_all;
+ /* 'no debug channel {all|chan_id}' */
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ is_all = !strcasecmp("all", argv[3]);
+ if (is_all) {
+ global_fin &= ~DEBUGCHAN_FLAG;
+ global_fout &= ~DEBUGCHAN_FLAG;
+ c = ast_channel_walk_locked(NULL);
+ } else {
+ c = ast_get_channel_by_name_locked(argv[3]);
+ if (c == NULL)
+ ast_cli(fd, "No such channel %s\n", argv[3]);
+ }
+ while(c) {
+ if ((c->fin & DEBUGCHAN_FLAG) || (c->fout & DEBUGCHAN_FLAG)) {
+ c->fin &= ~DEBUGCHAN_FLAG;
+ c->fout &= ~DEBUGCHAN_FLAG;
+ ast_cli(fd, "Debugging disabled on channel %s\n", c->name);
+ }
+ ast_channel_unlock(c);
+ if (!is_all)
+ break;
+ c = ast_channel_walk_locked(c);
+ }
+ ast_cli(fd, "Debugging on new channels is disabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_showchan_deprecated(int fd, int argc, char *argv[])
+{
+ struct ast_channel *c=NULL;
+ struct timeval now;
+ char buf[2048];
+ char cdrtime[256];
+ char nf[256], wf[256], rf[256];
+ long elapsed_seconds=0;
+ int hour=0, min=0, sec=0;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ now = ast_tvnow();
+ c = ast_get_channel_by_name_locked(argv[2]);
+ if (!c) {
+ ast_cli(fd, "%s is not a known channel\n", argv[2]);
+ return RESULT_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(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"
+ " 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)"), 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,buf,sizeof(buf)))
+ ast_cli(fd," Variables:\n%s\n",buf);
+ if(c->cdr && ast_cdr_serialize_variables(c->cdr,buf, sizeof(buf), '=', '\n', 1))
+ ast_cli(fd," CDR Variables:\n%s\n",buf);
+
+ ast_channel_unlock(c);
+ return RESULT_SUCCESS;
+}
+
+static int handle_showchan(int fd, int argc, char *argv[])
+{
+ struct ast_channel *c=NULL;
+ struct timeval now;
+ char buf[2048];
+ char cdrtime[256];
+ char nf[256], wf[256], rf[256];
+ long elapsed_seconds=0;
+ int hour=0, min=0, sec=0;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ now = ast_tvnow();
+ c = ast_get_channel_by_name_locked(argv[3]);
+ if (!c) {
+ ast_cli(fd, "%s is not a known channel\n", argv[3]);
+ return RESULT_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(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"
+ " 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)"), 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,buf,sizeof(buf)))
+ ast_cli(fd," Variables:\n%s\n",buf);
+ if(c->cdr && ast_cdr_serialize_variables(c->cdr,buf, sizeof(buf), '=', '\n', 1))
+ ast_cli(fd," CDR Variables:\n%s\n",buf);
+
+ ast_channel_unlock(c);
+ return RESULT_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;
+}
+
+static char *complete_show_channels_deprecated(const char *line, const char *word, int pos, int state)
+{
+ static char *choices[] = { "concise", "verbose", NULL };
+
+ return (pos != 2) ? NULL : ast_cli_complete(word, choices, state);
+}
+
+static char *complete_show_channels(const char *line, const char *word, int pos, int state)
+{
+ static char *choices[] = { "concise", "verbose", NULL };
+
+ return (pos != 3) ? NULL : ast_cli_complete(word, choices, state);
+}
+
+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 *complete_ch_3(const char *line, const char *word, int pos, int state)
+{
+ return ast_complete_channels(line, word, pos, state, 2);
+}
+
+static char *complete_ch_4(const char *line, const char *word, int pos, int state)
+{
+ return ast_complete_channels(line, word, pos, state, 3);
+}
+
+static char *complete_ch_5(const char *line, const char *word, int pos, int state)
+{
+ return ast_complete_channels(line, word, pos, state, 4);
+}
+
+static char *complete_mod_2(const char *line, const char *word, int pos, int state)
+{
+ return ast_module_helper(line, word, pos, state, 1, 1);
+}
+
+static char *complete_mod_3_nr(const char *line, const char *word, int pos, int state)
+{
+ return ast_module_helper(line, word, pos, state, 2, 0);
+}
+
+static char *complete_mod_3(const char *line, const char *word, int pos, int state)
+{
+ return ast_module_helper(line, word, pos, state, 2, 1);
+}
+
+static char *complete_mod_4(const char *line, const char *word, int pos, int state)
+{
+ return ast_module_helper(line, word, pos, state, 3, 0);
+}
+
+static char *complete_fn_2(const char *line, const char *word, int pos, int state)
+{
+ char *c, *d;
+ char filename[PATH_MAX];
+
+ if (pos != 1)
+ return NULL;
+
+ 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 = strdup(c);
+ free(d);
+
+ return c;
+}
+
+static char *complete_fn_3(const char *line, const char *word, int pos, int state)
+{
+ char *c, *d;
+ char filename[PATH_MAX];
+
+ if (pos != 2)
+ return NULL;
+
+ 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 = strdup(c);
+ free(d);
+
+ return c;
+}
+
+static int group_show_channels(int fd, int argc, char *argv[])
+{
+#define FORMAT_STRING "%-25s %-20s %-20s\n"
+
+ struct ast_group_info *gi = NULL;
+ int numchans = 0;
+ regex_t regexbuf;
+ int havepattern = 0;
+
+ if (argc < 3 || argc > 4)
+ return RESULT_SHOWUSAGE;
+
+ if (argc == 4) {
+ if (regcomp(&regexbuf, argv[3], REG_EXTENDED | REG_NOSUB))
+ return RESULT_SHOWUSAGE;
+ havepattern = 1;
+ }
+
+ ast_cli(fd, FORMAT_STRING, "Channel", "Group", "Category");
+
+ ast_app_group_list_lock();
+
+ gi = ast_app_group_list_head();
+ while (gi) {
+ if (!havepattern || !regexec(&regexbuf, gi->group, 0, NULL, 0)) {
+ ast_cli(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(fd, "%d active channel%s\n", numchans, (numchans != 1) ? "s" : "");
+ return RESULT_SUCCESS;
+#undef FORMAT_STRING
+}
+
+static int handle_help(int fd, int argc, char *argv[]);
+
+static char * complete_help(const char *text, const char *word, int pos, int state)
+{
+ /* skip first 4 or 5 chars, "help "*/
+ int l = strlen(text);
+
+ if (l > 5)
+ l = 5;
+ text += l;
+ /* XXX watch out, should stop to the non-generator parts */
+ return __ast_cli_generator(text, word, state, 0);
+}
+
+/* XXX Nothing in this array can currently be deprecated...
+ You have to change the way find_cli works in order to remove this array
+ I recommend doing this eventually...
+ */
+static struct ast_cli_entry builtins[] = {
+ /* Keep alphabetized, with longer matches first (example: abcd before abc) */
+ { { "_command", "complete", NULL },
+ handle_commandcomplete, "Command complete",
+ commandcomplete_help },
+
+ { { "_command", "nummatches", NULL },
+ handle_commandnummatches, "Returns number of command matches",
+ commandnummatches_help },
+
+ { { "_command", "matchesarray", NULL },
+ handle_commandmatchesarray, "Returns command matches array",
+ commandmatchesarray_help },
+
+ { { NULL }, NULL, NULL, NULL }
+};
+
+static struct ast_cli_entry cli_debug_channel_deprecated = {
+ { "debug", "channel", NULL },
+ handle_debugchan_deprecated, NULL,
+ NULL, complete_ch_3 };
+
+static struct ast_cli_entry cli_debug_level_deprecated = {
+ { "debug", "level", NULL },
+ handle_debuglevel_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_set_debug_deprecated = {
+ { "set", "debug", NULL },
+ handle_set_debug_deprecated, NULL,
+ NULL, NULL, &cli_debug_level_deprecated };
+
+static struct ast_cli_entry cli_set_verbose_deprecated = {
+ { "set", "verbose", NULL },
+ handle_set_verbose_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_channel_deprecated = {
+ { "show", "channel", NULL },
+ handle_showchan_deprecated, NULL,
+ NULL, complete_ch_3 };
+
+static struct ast_cli_entry cli_show_channels_deprecated = {
+ { "show", "channels", NULL },
+ handle_chanlist_deprecated, NULL,
+ NULL, complete_show_channels_deprecated };
+
+static struct ast_cli_entry cli_show_modules_deprecated = {
+ { "show", "modules", NULL },
+ handle_modlist, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_modules_like_deprecated = {
+ { "show", "modules", "like", NULL },
+ handle_modlist, NULL,
+ NULL, complete_mod_4 };
+
+static struct ast_cli_entry cli_module_load_deprecated = {
+ { "load", NULL },
+ handle_load_deprecated, NULL,
+ NULL, complete_fn_2 };
+
+static struct ast_cli_entry cli_module_reload_deprecated = {
+ { "reload", NULL },
+ handle_reload_deprecated, NULL,
+ NULL, complete_mod_2 };
+
+static struct ast_cli_entry cli_module_unload_deprecated = {
+ { "unload", NULL },
+ handle_unload_deprecated, NULL,
+ NULL, complete_mod_2 };
+
+static struct ast_cli_entry cli_show_uptime_deprecated = {
+ { "show", "uptime", NULL },
+ handle_showuptime_deprecated, "Show uptime information",
+ NULL };
+
+static struct ast_cli_entry cli_cli[] = {
+ /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
+ { { "no", "debug", "channel", NULL },
+ handle_nodebugchan_deprecated, NULL,
+ NULL, complete_ch_4 },
+
+ { { "core", "show", "channels", NULL },
+ handle_chanlist, "Display information on channels",
+ chanlist_help, complete_show_channels, &cli_show_channels_deprecated },
+
+ { { "core", "show", "channel", NULL },
+ handle_showchan, "Display information on a specific channel",
+ showchan_help, complete_ch_4, &cli_show_channel_deprecated },
+
+ { { "core", "set", "debug", "channel", NULL },
+ handle_core_set_debug_channel, "Enable/disable debugging on a channel",
+ debugchan_help, complete_ch_5, &cli_debug_channel_deprecated },
+
+ { { "core", "set", "debug", NULL },
+ handle_set_debug, "Set level of debug chattiness",
+ debug_help, NULL, &cli_set_debug_deprecated },
+
+ { { "core", "set", "debug", "off", NULL },
+ handle_nodebug, "Turns off debug chattiness",
+ nodebug_help },
+
+ { { "core", "set", "verbose", NULL },
+ handle_verbose, "Set level of verboseness",
+ verbose_help, NULL, &cli_set_verbose_deprecated },
+
+ { { "group", "show", "channels", NULL },
+ group_show_channels, "Display active channels with group(s)",
+ group_show_channels_help },
+
+ { { "help", NULL },
+ handle_help, "Display help list, or specific help on a command",
+ help_help, complete_help },
+
+ { { "logger", "mute", NULL },
+ handle_logger_mute, "Toggle logging output to a console",
+ logger_mute_help },
+
+ { { "module", "show", NULL },
+ handle_modlist, "List modules and info",
+ modlist_help, NULL, &cli_show_modules_deprecated },
+
+ { { "module", "show", "like", NULL },
+ handle_modlist, "List modules and info",
+ modlist_help, complete_mod_4, &cli_show_modules_like_deprecated },
+
+ { { "module", "load", NULL },
+ handle_load, "Load a module by name",
+ load_help, complete_fn_3, &cli_module_load_deprecated },
+
+ { { "module", "reload", NULL },
+ handle_reload, "Reload configuration",
+ reload_help, complete_mod_3, &cli_module_reload_deprecated },
+
+ { { "module", "unload", NULL },
+ handle_unload, "Unload a module by name",
+ unload_help, complete_mod_3_nr, &cli_module_unload_deprecated },
+
+ { { "core", "show", "uptime", NULL },
+ handle_showuptime, "Show uptime information",
+ uptime_help, NULL, &cli_show_uptime_deprecated },
+
+ { { "soft", "hangup", NULL },
+ handle_softhangup, "Request a hangup on a given channel",
+ softhangup_help, complete_ch_3 },
+};
+
+/*! \brief initialize the _full_cmd string in * each of the builtins. */
+void ast_builtins_init(void)
+{
+ struct ast_cli_entry *e;
+
+ for (e = builtins; e->cmda[0] != NULL; e++) {
+ char buf[80];
+ ast_join(buf, sizeof(buf), e->cmda);
+ e->_full_cmd = strdup(buf);
+ if (!e->_full_cmd)
+ ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
+ }
+
+ ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
+}
+
+/*
+ * We have two sets of commands: builtins are stored in a
+ * NULL-terminated array of ast_cli_entry, whereas external
+ * commands are in a list.
+ * When navigating, we need to keep two pointers and get
+ * the next one in lexicographic order. For the purpose,
+ * we use a structure.
+ */
+
+struct cli_iterator {
+ struct ast_cli_entry *builtins;
+ struct ast_cli_entry *helpers;
+};
+
+static struct ast_cli_entry *cli_next(struct cli_iterator *i)
+{
+ struct ast_cli_entry *e;
+
+ if (i->builtins == NULL && i->helpers == NULL) {
+ /* initialize */
+ i->builtins = builtins;
+ i->helpers = AST_LIST_FIRST(&helpers);
+ }
+ e = i->builtins; /* temporary */
+ if (!e->cmda[0] || (i->helpers &&
+ strcmp(i->helpers->_full_cmd, e->_full_cmd) < 0)) {
+ /* Use helpers */
+ e = i->helpers;
+ if (e)
+ i->helpers = AST_LIST_NEXT(e, list);
+ } else { /* use builtin. e is already set */
+ (i->builtins)++; /* move to next */
+ }
+ return e;
+}
+
+/*!
+ * \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.
+ * -1 true if the mismatch is on the last word XXX not true!
+ * 1 true only on complete, exact match.
+ */
+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;
+ struct cli_iterator i = { NULL, NULL};
+
+ while( (e = cli_next(&i)) ) {
+ int y;
+ for (y = 0 ; cmds[y] && e->cmda[y]; y++) {
+ if (strcasecmp(e->cmda[y], cmds[y]))
+ break;
+ }
+ if (e->cmda[y] == NULL) { /* no more words in candidate */
+ if (cmds[y] == NULL) /* this is an exact match, cannot do better */
+ break;
+ /* here the search key is longer than the candidate */
+ 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 candidate */
+ if (cmds[y] == NULL) /* search key is shorter, not good */
+ continue;
+ /* if we get here, both words exist but there is a mismatch */
+ if (match_type == 0) /* not the one we look for */
+ continue;
+ if (match_type == 1) /* not the one we look for */
+ continue;
+ if (cmds[y+1] != NULL || e->cmda[y+1] != NULL) /* not the one we look for */
+ continue;
+ /* we are in case match_type == -1 and mismatch on last word */
+ }
+ if (y > matchlen) { /* remember the candidate */
+ matchlen = y;
+ 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_LIST_LOCK(&helpers);
+ for (x=0;argv[x];x++) {
+ myargv[x] = argv[x];
+ if (!find_cli(myargv, -1))
+ break;
+ }
+ AST_LIST_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_LIST_LOCK(&helpers);
+ AST_LIST_REMOVE(&helpers, e, list);
+ AST_LIST_UNLOCK(&helpers);
+ free(e->_full_cmd);
+ }
+ return 0;
+}
+
+static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
+{
+ struct ast_cli_entry *cur;
+ char fulle[80] ="";
+ int lf, ret = -1;
+
+ ast_join(fulle, sizeof(fulle), e->cmda);
+ AST_LIST_LOCK(&helpers);
+
+ if (find_cli(e->cmda, 1)) {
+ ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", fulle);
+ goto done;
+ }
+ e->_full_cmd = ast_strdup(fulle);
+ if (!e->_full_cmd)
+ goto done;
+
+ if (ed) {
+ 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);
+ } else {
+ e->deprecated = 0;
+ }
+
+ lf = strlen(fulle);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
+ int len = strlen(cur->_full_cmd);
+ if (lf < len)
+ len = lf;
+ if (strncasecmp(fulle, cur->_full_cmd, len) < 0) {
+ AST_LIST_INSERT_BEFORE_CURRENT(&helpers, e, list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (!cur)
+ AST_LIST_INSERT_TAIL(&helpers, e, list);
+ ret = 0; /* success */
+
+done:
+ AST_LIST_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.
+ */
+void ast_cli_register_multiple(struct ast_cli_entry *e, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ ast_cli_register(e + i);
+}
+
+void ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ ast_cli_unregister(e + i);
+}
+
+
+/*! \brief helper for help_workhorse and final part of handle_help
+ * if locked = 0 it's just help_workhorse, otherwise assume the
+ * list is already locked.
+ */
+static int help1(int fd, char *match[], int locked)
+{
+ char matchstr[80] = "";
+ struct ast_cli_entry *e;
+ int len = 0;
+ int found = 0;
+ struct cli_iterator i = { NULL, NULL};
+
+ if (match) {
+ ast_join(matchstr, sizeof(matchstr), match);
+ len = strlen(matchstr);
+ }
+ if (!locked)
+ AST_LIST_LOCK(&helpers);
+ while ( (e = cli_next(&i)) ) {
+ /* 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, "%25.25s %s\n", e->_full_cmd, S_OR(e->summary, ""));
+ found++;
+ }
+ if (!locked)
+ AST_LIST_UNLOCK(&helpers);
+ if (!found && matchstr[0])
+ ast_cli(fd, "No such command '%s'.\n", matchstr);
+ return RESULT_SUCCESS;
+}
+
+static int help_workhorse(int fd, char *match[])
+{
+ return help1(fd, match, 0 /* do not print errors */);
+}
+
+static int handle_help(int fd, int argc, char *argv[])
+{
+ char fullcmd[80];
+ struct ast_cli_entry *e;
+ int res = RESULT_SUCCESS;
+
+ if (argc < 1)
+ return RESULT_SHOWUSAGE;
+ if (argc == 1)
+ return help_workhorse(fd, NULL);
+
+ AST_LIST_LOCK(&helpers);
+ e = find_cli(argv + 1, 1); /* try exact match first */
+ if (!e) {
+ res = help1(fd, argv + 1, 1 /* locked */);
+ AST_LIST_UNLOCK(&helpers);
+ return res;
+ }
+ if (e->usage)
+ ast_cli(fd, "%s", e->usage);
+ else {
+ ast_join(fullcmd, sizeof(fullcmd), argv+1);
+ ast_cli(fd, "No help text available for '%s'.\n", fullcmd);
+ }
+ AST_LIST_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;
+
+ *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)
+ free(oldbuf);
+ oldbuf = buf;
+ }
+ if (oldbuf)
+ 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;
+}
+
+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;
+ struct cli_iterator i = { NULL, NULL };
+ int x = 0, argindex, matchlen;
+ int matchnum=0;
+ char *ret = NULL;
+ char matchstr[80] = "";
+ int tws = 0;
+ char *dup = parse_args(text, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws);
+
+ if (!dup) /* error */
+ return NULL;
+ argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
+ /* rebuild the command, ignore tws */
+ ast_join(matchstr, sizeof(matchstr)-1, argv);
+ matchlen = strlen(matchstr);
+ if (tws) {
+ strcat(matchstr, " "); /* XXX */
+ if (matchlen)
+ matchlen++;
+ }
+ if (lock)
+ AST_LIST_LOCK(&helpers);
+ while( !ret && (e = cli_next(&i)) ) {
+ int lc = strlen(e->_full_cmd);
+ if (e->_full_cmd[0] != '_' && lc > 0 && matchlen <= lc &&
+ !strncasecmp(matchstr, e->_full_cmd, matchlen)) {
+ /* Found initial part, return a copy of the next word... */
+ if (e->cmda[argindex] && ++matchnum > state)
+ ret = strdup(e->cmda[argindex]); /* we need a malloced string */
+ } else if (e->generator && !strncasecmp(matchstr, e->_full_cmd, lc) && matchstr[lc] < 33) {
+ /* We have a command in its entirity within us -- theoretically only one
+ command can have this occur */
+ ret = e->generator(matchstr, word, argindex, state);
+ }
+ }
+ if (lock)
+ AST_LIST_UNLOCK(&helpers);
+ 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 *argv[AST_MAX_ARGS];
+ struct ast_cli_entry *e;
+ int x;
+ char *dup;
+ int tws;
+
+ if (!(dup = parse_args(s, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws)))
+ return -1;
+
+ /* We need at least one entry, or ignore */
+ if (x > 0) {
+ AST_LIST_LOCK(&helpers);
+ e = find_cli(argv, 0);
+ if (e)
+ e->inuse++;
+ AST_LIST_UNLOCK(&helpers);
+ if (e) {
+ switch(e->handler(fd, x, argv)) {
+ case RESULT_SHOWUSAGE:
+ if (e->usage)
+ ast_cli(fd, "%s", e->usage);
+ else
+ ast_cli(fd, "Invalid usage, but no usage information available.\n");
+ AST_LIST_LOCK(&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_LIST_UNLOCK(&helpers);
+ break;
+ default:
+ AST_LIST_LOCK(&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_LIST_UNLOCK(&helpers);
+ break;
+ }
+ } else
+ ast_cli(fd, "No such command '%s' (type 'help %s' for other possible commands)\n", s, find_best(argv));
+ if (e)
+ ast_atomic_fetchadd_int(&e->inuse, -1);
+ }
+ 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/main/coef_in.h b/main/coef_in.h
new file mode 100644
index 000000000..9aba022f6
--- /dev/null
+++ b/main/coef_in.h
@@ -0,0 +1,13 @@
+ { { 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,
+ }, },
diff --git a/main/coef_out.h b/main/coef_out.h
new file mode 100644
index 000000000..d80f5c490
--- /dev/null
+++ b/main/coef_out.h
@@ -0,0 +1,4 @@
+ { 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
+ },
+
diff --git a/main/config.c b/main/config.c
new file mode 100644
index 000000000..6163f827a
--- /dev/null
+++ b/main/config.c
@@ -0,0 +1,1520 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/stat.h>
+#define AST_INCLUDE_GLOB 1
+#ifdef AST_INCLUDE_GLOB
+#if defined(__Darwin__) || defined(__CYGWIN__)
+#define GLOB_ABORTED GLOB_ABEND
+#endif
+# include <glob.h>
+#endif
+
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+#include "asterisk/options.h"
+#include "asterisk/logger.h"
+#include "asterisk/utils.h"
+#include "asterisk/channel.h"
+#include "asterisk/app.h"
+
+#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];
+};
+
+#define CB_INCR 250
+
+static void CB_INIT(char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size)
+{
+ if (!(*comment_buffer)) {
+ *comment_buffer = ast_malloc(CB_INCR);
+ if (!(*comment_buffer))
+ return;
+ (*comment_buffer)[0] = 0;
+ *comment_buffer_size = CB_INCR;
+ *lline_buffer = ast_malloc(CB_INCR);
+ if (!(*lline_buffer))
+ return;
+ (*lline_buffer)[0] = 0;
+ *lline_buffer_size = CB_INCR;
+ } else {
+ (*comment_buffer)[0] = 0;
+ (*lline_buffer)[0] = 0;
+ }
+}
+
+static void CB_ADD(char **comment_buffer, int *comment_buffer_size, char *str)
+{
+ int rem = *comment_buffer_size - strlen(*comment_buffer) - 1;
+ int siz = strlen(str);
+ if (rem < siz+1) {
+ *comment_buffer = ast_realloc(*comment_buffer, *comment_buffer_size + CB_INCR + siz + 1);
+ if (!(*comment_buffer))
+ return;
+ *comment_buffer_size += CB_INCR+siz+1;
+ }
+ strcat(*comment_buffer,str);
+}
+
+static void CB_ADD_LEN(char **comment_buffer, int *comment_buffer_size, char *str, int len)
+{
+ int cbl = strlen(*comment_buffer) + 1;
+ int rem = *comment_buffer_size - cbl;
+ if (rem < len+1) {
+ *comment_buffer = ast_realloc(*comment_buffer, *comment_buffer_size + CB_INCR + len + 1);
+ if (!(*comment_buffer))
+ return;
+ *comment_buffer_size += CB_INCR+len+1;
+ }
+ strncat(*comment_buffer,str,len);
+ (*comment_buffer)[cbl+len-1] = 0;
+}
+
+static void LLB_ADD(char **lline_buffer, int *lline_buffer_size, char *str)
+{
+ int rem = *lline_buffer_size - strlen(*lline_buffer) - 1;
+ int siz = strlen(str);
+ if (rem < siz+1) {
+ *lline_buffer = ast_realloc(*lline_buffer, *lline_buffer_size + CB_INCR + siz + 1);
+ if (!(*lline_buffer))
+ return;
+ *lline_buffer_size += CB_INCR + siz + 1;
+ }
+ strcat(*lline_buffer,str);
+}
+
+static void CB_RESET(char **comment_buffer, char **lline_buffer)
+{
+ (*comment_buffer)[0] = 0;
+ (*lline_buffer)[0] = 0;
+}
+
+
+static struct ast_comment *ALLOC_COMMENT(const char *buffer)
+{
+ struct ast_comment *x = ast_calloc(1,sizeof(struct ast_comment)+strlen(buffer)+1);
+ strcpy(x->cmt, buffer);
+ return x;
+}
+
+
+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 */
+ int include_level;
+ AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ 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_variable *ast_variable_new(const char *name, const char *value)
+{
+ struct ast_variable *variable;
+ int name_len = strlen(name) + 1;
+
+ if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + sizeof(*variable)))) {
+ variable->name = variable->stuff;
+ variable->value = variable->stuff + name_len;
+ strcpy(variable->name,name);
+ strcpy(variable->value,value);
+ }
+
+ return variable;
+}
+
+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;
+ 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);
+
+ 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;
+#if 1
+ /* we can just move the entire list in a single op */
+ ast_variable_append(new, var);
+#else
+ while (var) {
+ struct ast_variable *next = var->next;
+ var->next = NULL;
+ ast_variable_append(new, var);
+ var = next;
+ }
+#endif
+}
+
+struct ast_category *ast_category_new(const char *name)
+{
+ struct ast_category *category;
+
+ if ((category = ast_calloc(1, sizeof(*category))))
+ ast_copy_string(category->name, name, sizeof(category->name));
+ 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);
+ }
+ cat->precomments = NULL;
+ cat->sameline = NULL;
+}
+
+static void ast_destroy_template_list(struct ast_category *cat)
+{
+ struct ast_category_template_instance *x;
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&cat->template_instances, x, next) {
+ AST_LIST_REMOVE_CURRENT(&cat->template_instances, next);
+ free(x);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+}
+
+void ast_category_destroy(struct ast_category *cat)
+{
+ ast_variables_destroy(cat->root);
+ ast_destroy_comments(cat);
+ ast_destroy_template_list(cat);
+ free(cat);
+}
+
+static struct ast_category *next_available_category(struct ast_category *cat)
+{
+ for (; cat && cat->ignored; cat = cat->next);
+
+ return cat;
+}
+
+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, char *variable, 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;
+
+ if (!(newer = ast_variable_new(variable, value)))
+ return -1;
+
+ newer->object = object;
+
+ for (cur = category->root; cur; prev = cur, cur = cur->next) {
+ if (strcasecmp(cur->name, variable) ||
+ (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
+ continue;
+
+ 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, 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;
+
+ cat = cfg->root;
+ while(cat) {
+ catn = cat;
+ cat = cat->next;
+ ast_category_destroy(catn);
+ }
+ 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;
+}
+
+static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, int withcomments,
+ char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size)
+{
+ char *c;
+ char *cur = buf;
+ struct ast_variable *v;
+ char cmd[512], exec_file[512];
+ int object, do_exec, do_include;
+
+ /* Actually parse the entry */
+ if (cur[0] == '[') {
+ struct ast_category *newcat = NULL;
+ char *catname;
+
+ /* A category header */
+ 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))) {
+ return -1;
+ }
+ /* add comments */
+ if (withcomments && *comment_buffer && (*comment_buffer)[0] ) {
+ newcat->precomments = ALLOC_COMMENT(*comment_buffer);
+ }
+ if (withcomments && *lline_buffer && (*lline_buffer)[0] ) {
+ newcat->sameline = ALLOC_COMMENT(*lline_buffer);
+ }
+ if( 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 */
+ 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;
+ do_include = !strcasecmp(cur, "include");
+ if(!do_include)
+ do_exec = !strcasecmp(cur, "exec");
+ else
+ do_exec = 0;
+ if (do_exec && !ast_opt_exec_includes) {
+ ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
+ do_exec = 0;
+ }
+ if (do_include || do_exec) {
+ if (c) {
+ /* Strip off leading and trailing "'s and <>'s */
+ while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
+ /* Get rid of leading mess */
+ cur = c;
+ 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_exec) {
+ 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
+ exec_file[0] = '\0';
+ /* A #include */
+ do_include = ast_config_internal_load(cur, cfg, withcomments) ? 1 : 0;
+ if(!ast_strlen_zero(exec_file))
+ unlink(exec_file);
+ if (!do_include) {
+ ast_log(LOG_ERROR, "*********************************************************\n");
+ ast_log(LOG_ERROR, "*********** YOU SHOULD REALLY READ THIS ERROR ***********\n");
+ ast_log(LOG_ERROR, "Future versions of Asterisk will treat a #include of a "
+ "file that does not exist as an error, and will fail to "
+ "load that configuration file. Please ensure that the "
+ "file '%s' exists, even if it is empty.\n", cur);
+ ast_log(LOG_ERROR, "*********** YOU SHOULD REALLY READ THIS ERROR ***********\n");
+ ast_log(LOG_ERROR, "*********************************************************\n");
+ return 0;
+ }
+
+ } else {
+ ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
+ do_exec ? "exec" : "include",
+ do_exec ? "/path/to/executable" : "filename",
+ lineno,
+ configfile);
+ }
+ }
+ else
+ ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
+ } 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) {
+ *c = 0;
+ c++;
+ /* Ignore > in => */
+ if (*c== '>') {
+ object = 1;
+ c++;
+ } else
+ object = 0;
+ if ((v = ast_variable_new(ast_strip(cur), ast_strip(c)))) {
+ v->lineno = lineno;
+ v->object = object;
+ /* Put and reset comments */
+ v->blanklines = 0;
+ ast_variable_append(*cat, v);
+ /* add comments */
+ if (withcomments && *comment_buffer && (*comment_buffer)[0] ) {
+ v->precomments = ALLOC_COMMENT(*comment_buffer);
+ }
+ if (withcomments && *lline_buffer && (*lline_buffer)[0] ) {
+ v->sameline = ALLOC_COMMENT(*lline_buffer);
+ }
+ if( 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, int withcomments)
+{
+ char fn[256];
+#if defined(LOW_MEMORY)
+ char buf[512];
+#else
+ char buf[8192];
+#endif
+ 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;
+ /*! Growable string buffer */
+ char *comment_buffer=0; /*!< this will be a comment collector.*/
+ int comment_buffer_size=0; /*!< the amount of storage so far alloc'd for the comment_buffer */
+
+ char *lline_buffer=0; /*!< A buffer for stuff behind the ; */
+ int lline_buffer_size=0;
+
+
+ cat = ast_config_get_current_category(cfg);
+
+ if (filename[0] == '/') {
+ ast_copy_string(fn, filename, sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename);
+ }
+
+ if (withcomments) {
+ CB_INIT(&comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size);
+ if (!lline_buffer || !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 */
+#ifdef SOLARIS
+ glob_ret = glob(fn, GLOB_NOCHECK, NULL, &globbuf);
+#else
+ glob_ret = glob(fn, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
+#endif
+ 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
+ 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 (option_verbose > 1) {
+ ast_verbose(VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
+ fflush(stdout);
+ }
+ if (!(f = fopen(fn, "r"))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
+ if (option_verbose > 1)
+ ast_verbose( "Not found (%s)\n", strerror(errno));
+ continue;
+ }
+ count++;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Parsing %s\n", fn);
+ if (option_verbose > 1)
+ ast_verbose("Found\n");
+ while(!feof(f)) {
+ lineno++;
+ if (fgets(buf, sizeof(buf), f)) {
+ if ( withcomments ) {
+ CB_ADD(&comment_buffer, &comment_buffer_size, lline_buffer); /* add the current lline buffer to the comment buffer */
+ lline_buffer[0] = 0; /* erase the lline buffer */
+ }
+
+ new_buf = buf;
+ if (comment)
+ process_buf = NULL;
+ else
+ process_buf = buf;
+
+ 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 ( withcomments ) {
+ CB_ADD(&comment_buffer, &comment_buffer_size, ";");
+ CB_ADD_LEN(&comment_buffer, &comment_buffer_size, 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 ( withcomments ) {
+ LLB_ADD(&lline_buffer, &lline_buffer_size, comment_p);
+ }
+ *comment_p = '\0';
+ new_buf = comment_p;
+ } else
+ new_buf = comment_p + 1;
+ }
+ }
+ if( withcomments && comment && !process_buf )
+ {
+ CB_ADD(&comment_buffer, &comment_buffer_size, 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, withcomments, &comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size)) {
+ cfg = NULL;
+ break;
+ }
+ }
+ }
+ }
+ }
+ 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)
+ break;
+ }
+ globfree(&globbuf);
+ }
+ }
+#endif
+
+ if (cfg && cfg->include_level == 1 && withcomments && comment_buffer) {
+ free(comment_buffer);
+ free(lline_buffer);
+ comment_buffer = NULL;
+ lline_buffer = NULL;
+ comment_buffer_size = 0;
+ lline_buffer_size = 0;
+ }
+
+ if (count == 0)
+ return NULL;
+
+ return cfg;
+}
+
+int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
+{
+ FILE *f = NULL;
+ int fd = -1;
+ char fn[256], fntmp[256];
+ char date[256]="";
+ time_t t;
+ struct ast_variable *var;
+ struct ast_category *cat;
+ struct ast_comment *cmt;
+ struct stat s;
+ int blanklines = 0;
+ int stat_result = 0;
+
+ if (configfile[0] == '/') {
+ snprintf(fntmp, sizeof(fntmp), "%s.XXXXXX", configfile);
+ ast_copy_string(fn, configfile, sizeof(fn));
+ } else {
+ snprintf(fntmp, sizeof(fntmp), "%s/%s.XXXXXX", ast_config_AST_CONFIG_DIR, configfile);
+ snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
+ }
+ time(&t);
+ ast_copy_string(date, ctime(&t), sizeof(date));
+ if ((fd = mkstemp(fntmp)) > 0 && (f = fdopen(fd, "w")) != NULL) {
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Saving '%s': ", fn);
+ fprintf(f, ";!\n");
+ fprintf(f, ";! Automatically generated configuration file\n");
+ if (strcmp(configfile, fn))
+ fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
+ else
+ fprintf(f, ";! Filename: %s\n", configfile);
+ fprintf(f, ";! Generator: %s\n", generator);
+ fprintf(f, ";! Creation Date: %s", date);
+ fprintf(f, ";!\n");
+ cat = cfg->root;
+ while(cat) {
+ /* Dump section with any appropriate comment */
+ for (cmt = cat->precomments; cmt; cmt=cmt->next)
+ {
+ if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (!cat->precomments)
+ fprintf(f,"\n");
+ fprintf(f, "[%s]", cat->name);
+ if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
+ fprintf(f, "(");
+ if (cat->ignored) {
+ fprintf(f, "!");
+ }
+ if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
+ fprintf(f, ",");
+ }
+ if (!AST_LIST_EMPTY(&cat->template_instances)) {
+ struct ast_category_template_instance *x;
+ 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");
+ var = cat->root;
+ while(var) {
+ struct ast_category_template_instance *x;
+ struct ast_variable *v2;
+ int found = 0;
+ AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
+
+ for (v2 = x->inst->root; v2; v2 = v2->next) {
+ if (!strcasecmp(var->name, v2->name))
+ break;
+ }
+ if (v2 && v2->value && !strcmp(v2->value, var->value)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ var = var->next;
+ continue;
+ }
+ 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);
+ if (var->blanklines) {
+ blanklines = var->blanklines;
+ while (blanklines--)
+ fprintf(f, "\n");
+ }
+
+ var = var->next;
+ }
+#if 0
+ /* Put an empty line */
+ fprintf(f, "\n");
+#endif
+ cat = cat->next;
+ }
+ if ((option_verbose > 1) && !option_debug)
+ ast_verbose("Saved\n");
+ } else {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Unable to open for writing: %s (%s)\n", fn, strerror(errno));
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
+ if (fd > -1)
+ close(fd);
+ return -1;
+ }
+ if (!(stat_result = stat(fn, &s))) {
+ fchmod(fd, s.st_mode);
+ }
+ fclose(f);
+ if ((!stat_result && unlink(fn)) || link(fntmp, fn)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Unable to open for writing: %s (%s)\n", fn, strerror(errno));
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
+ unlink(fntmp);
+ return -1;
+ }
+ unlink(fntmp);
+ 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;
+ free(map);
+ }
+
+ ast_mutex_unlock(&config_lock);
+}
+
+static int append_mapping(char *name, char *driver, char *database, 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;
+
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_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;
+
+ clear_config_maps();
+
+ configtmp = ast_config_new();
+ configtmp->max_include_level = 1;
+ config = ast_config_internal_load(extconfig_conf, configtmp, 0);
+ if (!config) {
+ ast_config_destroy(configtmp);
+ return 0;
+ }
+
+ for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
+ stringp = v->value;
+ 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, int withcomments)
+{
+ char db[256];
+ char table[256];
+ struct ast_config_engine *loader = &text_file_engine;
+ struct ast_config *result;
+
+ /* The config file itself bumps include_level by 1 */
+ if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
+ 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, withcomments);
+
+ if (result)
+ result->include_level--;
+ else
+ cfg->include_level--;
+
+ return result;
+}
+
+struct ast_config *ast_config_load(const char *filename)
+{
+ struct ast_config *cfg;
+ struct ast_config *result;
+
+ cfg = ast_config_new();
+ if (!cfg)
+ return NULL;
+
+ result = ast_config_internal_load(filename, cfg, 0);
+ if (!result)
+ ast_config_destroy(cfg);
+
+ return result;
+}
+
+struct ast_config *ast_config_load_with_comments(const char *filename)
+{
+ struct ast_config *cfg;
+ struct ast_config *result;
+
+ cfg = ast_config_new();
+ if (!cfg)
+ return NULL;
+
+ result = ast_config_internal_load(filename, cfg, 1);
+ if (!result)
+ ast_config_destroy(cfg);
+
+ return result;
+}
+
+struct ast_variable *ast_load_realtime(const char *family, ...)
+{
+ struct ast_config_engine *eng;
+ char db[256]="";
+ char table[256]="";
+ struct ast_variable *res=NULL;
+ va_list ap;
+
+ va_start(ap, family);
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+ if (eng && eng->realtime_func)
+ res = eng->realtime_func(db, table, ap);
+ va_end(ap);
+
+ 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;
+
+}
+
+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;
+}
+
+static int config_command(int fd, int argc, char **argv)
+{
+ struct ast_config_engine *eng;
+ struct ast_config_map *map;
+
+ ast_mutex_lock(&config_lock);
+
+ ast_cli(fd, "\n\n");
+ for (eng = config_engine_list; eng; eng = eng->next) {
+ ast_cli(fd, "\nConfig Engine: %s\n", eng->name);
+ for (map = config_maps; map; map = map->next)
+ if (!strcasecmp(map->driver, eng->name)) {
+ ast_cli(fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
+ map->table ? map->table : map->name);
+ }
+ }
+ ast_cli(fd,"\n\n");
+
+ ast_mutex_unlock(&config_lock);
+
+ return 0;
+}
+
+static char show_config_help[] =
+ "Usage: core show config mappings\n"
+ " Shows the filenames to config engines.\n";
+
+static struct ast_cli_entry cli_show_config_mappings_deprecated = {
+ { "show", "config", "mappings", NULL },
+ config_command, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_config[] = {
+ { { "core", "show", "config", "mappings", NULL },
+ config_command, "Display config mappings (file names to config engines)",
+ show_config_help, NULL, &cli_show_config_mappings_deprecated },
+};
+
+int register_config_cli()
+{
+ ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
+ return 0;
+}
diff --git a/main/cryptostub.c b/main/cryptostub.c
new file mode 100644
index 000000000..676110374
--- /dev/null
+++ b/main/cryptostub.c
@@ -0,0 +1,95 @@
+/*
+ * 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 <unistd.h>
+#include <stdlib.h>
+
+#include "asterisk/crypto.h"
+#include "asterisk/logger.h"
+
+/* Hrm, I wonder if the compiler is smart enough to only create two functions
+ for all these... I could force it to only make two, but those would be some
+ really nasty looking casts. */
+
+static struct ast_key *stub_ast_key_get(const char *kname, int ktype)
+{
+ ast_log(LOG_NOTICE, "Crypto support not loaded!\n");
+ return NULL;
+}
+
+static int stub_ast_check_signature(struct ast_key *key, const char *msg, const char *sig)
+{
+ ast_log(LOG_NOTICE, "Crypto support not loaded!\n");
+ return -1;
+}
+
+static int stub_ast_check_signature_bin(struct ast_key *key, const char *msg, int msglen, const unsigned char *sig)
+{
+ ast_log(LOG_NOTICE, "Crypto support not loaded!\n");
+ return -1;
+}
+
+static int stub_ast_sign(struct ast_key *key, char *msg, char *sig)
+{
+ ast_log(LOG_NOTICE, "Crypto support not loaded!\n");
+ return -1;
+}
+
+static int stub_ast_sign_bin(struct ast_key *key, const char *msg, int msglen, unsigned char *sig)
+{
+ ast_log(LOG_NOTICE, "Crypto support not loaded!\n");
+ return -1;
+}
+
+static int stub_ast_encdec_bin(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
+{
+ ast_log(LOG_NOTICE, "Crypto support not loaded!\n");
+ return -1;
+}
+
+struct ast_key *(*ast_key_get)(const char *key, int type) =
+ stub_ast_key_get;
+
+int (*ast_check_signature)(struct ast_key *key, const char *msg, const char *sig) =
+ stub_ast_check_signature;
+
+int (*ast_check_signature_bin)(struct ast_key *key, const char *msg, int msglen, const unsigned char *sig) =
+ stub_ast_check_signature_bin;
+
+int (*ast_sign)(struct ast_key *key, char *msg, char *sig) =
+ stub_ast_sign;
+
+int (*ast_sign_bin)(struct ast_key *key, const char *msg, int msglen, unsigned char *sig) =
+ stub_ast_sign_bin;
+
+int (*ast_encrypt_bin)(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key) =
+ stub_ast_encdec_bin;
+
+int (*ast_decrypt_bin)(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key) =
+ stub_ast_encdec_bin;
diff --git a/main/db.c b/main/db.c
new file mode 100644
index 000000000..3e61be509
--- /dev/null
+++ b/main/db.c
@@ -0,0 +1,592 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.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"
+
+#ifdef __CYGWIN__
+#define dbopen __dbopen
+#endif
+
+static DB *astdb;
+AST_MUTEX_DEFINE_STATIC(dblock);
+
+static int dbinit(void)
+{
+ if (!astdb && !(astdb = dbopen((char *)ast_config_AST_DB, O_CREAT | O_RDWR, 0664, 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;
+
+ 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);
+ }
+ }
+ astdb->sync(astdb, 0);
+ ast_mutex_unlock(&dblock);
+ return 0;
+}
+
+int ast_db_put(const char *family, const char *keys, 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 = 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) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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 && option_debug)
+ ast_log(LOG_DEBUG, "Unable to find key '%s' in family '%s'\n", keys, family);
+ return res;
+}
+
+static int database_put(int fd, int argc, char *argv[])
+{
+ int res;
+ if (argc != 5)
+ return RESULT_SHOWUSAGE;
+ res = ast_db_put(argv[2], argv[3], argv[4]);
+ if (res) {
+ ast_cli(fd, "Failed to update entry\n");
+ } else {
+ ast_cli(fd, "Updated database successfully\n");
+ }
+ return RESULT_SUCCESS;
+}
+
+static int database_get(int fd, int argc, char *argv[])
+{
+ int res;
+ char tmp[256];
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
+ if (res) {
+ ast_cli(fd, "Database entry not found.\n");
+ } else {
+ ast_cli(fd, "Value: %s\n", tmp);
+ }
+ return RESULT_SUCCESS;
+}
+
+static int database_del(int fd, int argc, char *argv[])
+{
+ int res;
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ res = ast_db_del(argv[2], argv[3]);
+ if (res) {
+ ast_cli(fd, "Database entry does not exist.\n");
+ } else {
+ ast_cli(fd, "Database entry removed.\n");
+ }
+ return RESULT_SUCCESS;
+}
+
+static int database_deltree(int fd, int argc, char *argv[])
+{
+ int res;
+ if ((argc < 3) || (argc > 4))
+ return RESULT_SHOWUSAGE;
+ if (argc == 4) {
+ res = ast_db_deltree(argv[2], argv[3]);
+ } else {
+ res = ast_db_deltree(argv[2], NULL);
+ }
+ if (res) {
+ ast_cli(fd, "Database entries do not exist.\n");
+ } else {
+ ast_cli(fd, "Database entries removed.\n");
+ }
+ return RESULT_SUCCESS;
+}
+
+static int database_show(int fd, int argc, char *argv[])
+{
+ char prefix[256];
+ DBT key, data;
+ char *keys, *values;
+ int res;
+ int pass;
+
+ if (argc == 4) {
+ /* Family and key tree */
+ snprintf(prefix, sizeof(prefix), "/%s/%s", argv[2], argv[3]);
+ } else if (argc == 3) {
+ /* Family only */
+ snprintf(prefix, sizeof(prefix), "/%s", argv[2]);
+ } else if (argc == 2) {
+ /* Neither */
+ prefix[0] = '\0';
+ } else {
+ return RESULT_SHOWUSAGE;
+ }
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ ast_cli(fd, "Database unavailable\n");
+ return RESULT_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(fd, "%-50s: %-25s\n", keys, values);
+ }
+ }
+ ast_mutex_unlock(&dblock);
+ return RESULT_SUCCESS;
+}
+
+static int database_showkey(int fd, int argc, char *argv[])
+{
+ char suffix[256];
+ DBT key, data;
+ char *keys, *values;
+ int res;
+ int pass;
+
+ if (argc == 3) {
+ /* Key only */
+ snprintf(suffix, sizeof(suffix), "/%s", argv[2]);
+ } else {
+ return RESULT_SHOWUSAGE;
+ }
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ ast_cli(fd, "Database unavailable\n");
+ return RESULT_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(fd, "%-50s: %-25s\n", keys, values);
+ }
+ }
+ ast_mutex_unlock(&dblock);
+ return RESULT_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, keytree);
+ } 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;
+ free(last);
+ }
+}
+
+static char database_show_usage[] =
+"Usage: database show [family [keytree]]\n"
+" Shows Asterisk database contents, optionally restricted\n"
+"to a given family, or family and keytree.\n";
+
+static char database_showkey_usage[] =
+"Usage: database showkey <keytree>\n"
+" Shows Asterisk database contents, restricted to a given key.\n";
+
+static char database_put_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";
+
+static char database_get_usage[] =
+"Usage: database get <family> <key>\n"
+" Retrieves an entry in the Asterisk database for a given\n"
+"family and key.\n";
+
+static char database_del_usage[] =
+"Usage: database del <family> <key>\n"
+" Deletes an entry in the Asterisk database for a given\n"
+"family and key.\n";
+
+static char database_deltree_usage[] =
+"Usage: database deltree <family> [keytree]\n"
+" Deletes a family or specific keytree within a family\n"
+"in the Asterisk database.\n";
+
+struct ast_cli_entry cli_database[] = {
+ { { "database", "show", NULL },
+ database_show, "Shows database contents",
+ database_show_usage },
+
+ { { "database", "showkey", NULL },
+ database_showkey, "Shows database contents",
+ database_showkey_usage },
+
+ { { "database", "get", NULL },
+ database_get, "Gets database value",
+ database_get_usage },
+
+ { { "database", "put", NULL },
+ database_put, "Adds/updates database value",
+ database_put_usage },
+
+ { { "database", "del", NULL },
+ database_del, "Removes database key/value",
+ database_del_usage },
+
+ { { "database", "deltree", NULL },
+ database_deltree, "Removes database keytree/values",
+ database_deltree_usage },
+};
+
+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, (char *) 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;
+}
+
+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, manager_dbget, "Get DB Entry");
+ ast_manager_register("DBPut", EVENT_FLAG_SYSTEM, manager_dbput, "Put DB Entry");
+ return 0;
+}
diff --git a/main/db1-ast/Makefile b/main/db1-ast/Makefile
new file mode 100644
index 000000000..c9d29b369
--- /dev/null
+++ b/main/db1-ast/Makefile
@@ -0,0 +1,71 @@
+# @(#)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) */*.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/main/db1-ast/btree/bt_close.c b/main/db1-ast/btree/bt_close.c
new file mode 100644
index 000000000..67a6e5340
--- /dev/null
+++ b/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/main/db1-ast/btree/bt_conv.c b/main/db1-ast/btree/bt_conv.c
new file mode 100644
index 000000000..d2ebdc57b
--- /dev/null
+++ b/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/main/db1-ast/btree/bt_debug.c b/main/db1-ast/btree/bt_debug.c
new file mode 100644
index 000000000..e035851a8
--- /dev/null
+++ b/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/main/db1-ast/btree/bt_delete.c b/main/db1-ast/btree/bt_delete.c
new file mode 100644
index 000000000..e816c432a
--- /dev/null
+++ b/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/main/db1-ast/btree/bt_get.c b/main/db1-ast/btree/bt_get.c
new file mode 100644
index 000000000..b5e18022c
--- /dev/null
+++ b/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/main/db1-ast/btree/bt_open.c b/main/db1-ast/btree/bt_open.c
new file mode 100644
index 000000000..5d40e4593
--- /dev/null
+++ b/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/main/db1-ast/btree/bt_overflow.c b/main/db1-ast/btree/bt_overflow.c
new file mode 100644
index 000000000..d8f310d91
--- /dev/null
+++ b/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/main/db1-ast/btree/bt_page.c b/main/db1-ast/btree/bt_page.c
new file mode 100644
index 000000000..e77a1d6b5
--- /dev/null
+++ b/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/main/db1-ast/btree/bt_put.c b/main/db1-ast/btree/bt_put.c
new file mode 100644
index 000000000..aeb0bb16c
--- /dev/null
+++ b/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/main/db1-ast/btree/bt_search.c b/main/db1-ast/btree/bt_search.c
new file mode 100644
index 000000000..623f43949
--- /dev/null
+++ b/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/main/db1-ast/btree/bt_seq.c b/main/db1-ast/btree/bt_seq.c
new file mode 100644
index 000000000..3f1724274
--- /dev/null
+++ b/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/main/db1-ast/btree/bt_split.c b/main/db1-ast/btree/bt_split.c
new file mode 100644
index 000000000..8fede1e45
--- /dev/null
+++ b/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/main/db1-ast/btree/bt_utils.c b/main/db1-ast/btree/bt_utils.c
new file mode 100644
index 000000000..2ecb5e678
--- /dev/null
+++ b/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/main/db1-ast/btree/btree.h b/main/db1-ast/btree/btree.h
new file mode 100644
index 000000000..1f4a9ec91
--- /dev/null
+++ b/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/main/db1-ast/btree/extern.h b/main/db1-ast/btree/extern.h
new file mode 100644
index 000000000..ebd9c5492
--- /dev/null
+++ b/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/main/db1-ast/db/db.c b/main/db1-ast/db/db.c
new file mode 100644
index 000000000..8c0584d4c
--- /dev/null
+++ b/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/main/db1-ast/hash/README b/main/db1-ast/hash/README
new file mode 100644
index 000000000..f29ccf7e1
--- /dev/null
+++ b/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/main/db1-ast/hash/extern.h b/main/db1-ast/hash/extern.h
new file mode 100644
index 000000000..4f1f23d67
--- /dev/null
+++ b/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/main/db1-ast/hash/hash.c b/main/db1-ast/hash/hash.c
new file mode 100644
index 000000000..47dc52a0e
--- /dev/null
+++ b/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/main/db1-ast/hash/hash.h b/main/db1-ast/hash/hash.h
new file mode 100644
index 000000000..d07db6f07
--- /dev/null
+++ b/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/main/db1-ast/hash/hash_bigkey.c b/main/db1-ast/hash/hash_bigkey.c
new file mode 100644
index 000000000..daa8c1f17
--- /dev/null
+++ b/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/main/db1-ast/hash/hash_buf.c b/main/db1-ast/hash/hash_buf.c
new file mode 100644
index 000000000..063dd8229
--- /dev/null
+++ b/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/main/db1-ast/hash/hash_func.c b/main/db1-ast/hash/hash_func.c
new file mode 100644
index 000000000..4d7907b57
--- /dev/null
+++ b/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/main/db1-ast/hash/hash_log2.c b/main/db1-ast/hash/hash_log2.c
new file mode 100644
index 000000000..b86655d47
--- /dev/null
+++ b/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/main/db1-ast/hash/hash_page.c b/main/db1-ast/hash/hash_page.c
new file mode 100644
index 000000000..52571c552
--- /dev/null
+++ b/main/db1-ast/hash/hash_page.c
@@ -0,0 +1,946 @@
+/*-
+ * 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) {
+ if (write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1) < 0) {
+ }
+ 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) {
+ if (write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1) < 0) {
+ }
+ 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) {
+ if (write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1) < 0) {
+ }
+ 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/main/db1-ast/hash/hsearch.c b/main/db1-ast/hash/hsearch.c
new file mode 100644
index 000000000..2971f9308
--- /dev/null
+++ b/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/main/db1-ast/hash/ndbm.c b/main/db1-ast/hash/ndbm.c
new file mode 100644
index 000000000..d702f737a
--- /dev/null
+++ b/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/main/db1-ast/hash/page.h b/main/db1-ast/hash/page.h
new file mode 100644
index 000000000..0fc0d5a3e
--- /dev/null
+++ b/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/main/db1-ast/hash/search.h b/main/db1-ast/hash/search.h
new file mode 100644
index 000000000..4d3b9143e
--- /dev/null
+++ b/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/main/db1-ast/include/circ-queue.h b/main/db1-ast/include/circ-queue.h
new file mode 100644
index 000000000..33ba9115b
--- /dev/null
+++ b/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/main/db1-ast/include/compat.h b/main/db1-ast/include/compat.h
new file mode 100644
index 000000000..706e58265
--- /dev/null
+++ b/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/main/db1-ast/include/db.h b/main/db1-ast/include/db.h
new file mode 100644
index 000000000..a58724bdd
--- /dev/null
+++ b/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/main/db1-ast/include/mpool.h b/main/db1-ast/include/mpool.h
new file mode 100644
index 000000000..0cfc5741c
--- /dev/null
+++ b/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/main/db1-ast/include/ndbm.h b/main/db1-ast/include/ndbm.h
new file mode 100644
index 000000000..d2079b0a5
--- /dev/null
+++ b/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/main/db1-ast/libdb.map b/main/db1-ast/libdb.map
new file mode 100644
index 000000000..87e34c430
--- /dev/null
+++ b/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/main/db1-ast/mpool/README b/main/db1-ast/mpool/README
new file mode 100644
index 000000000..0f01fbcdb
--- /dev/null
+++ b/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/main/db1-ast/mpool/mpool.c b/main/db1-ast/mpool/mpool.c
new file mode 100644
index 000000000..b5b7c86d6
--- /dev/null
+++ b/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/main/db1-ast/recno/extern.h b/main/db1-ast/recno/extern.h
new file mode 100644
index 000000000..feed43445
--- /dev/null
+++ b/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/main/db1-ast/recno/rec_close.c b/main/db1-ast/recno/rec_close.c
new file mode 100644
index 000000000..20b00f52c
--- /dev/null
+++ b/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/main/db1-ast/recno/rec_delete.c b/main/db1-ast/recno/rec_delete.c
new file mode 100644
index 000000000..fc2047226
--- /dev/null
+++ b/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/main/db1-ast/recno/rec_get.c b/main/db1-ast/recno/rec_get.c
new file mode 100644
index 000000000..7038cc81a
--- /dev/null
+++ b/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/main/db1-ast/recno/rec_open.c b/main/db1-ast/recno/rec_open.c
new file mode 100644
index 000000000..0ebc8c7c4
--- /dev/null
+++ b/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/main/db1-ast/recno/rec_put.c b/main/db1-ast/recno/rec_put.c
new file mode 100644
index 000000000..331699867
--- /dev/null
+++ b/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/main/db1-ast/recno/rec_search.c b/main/db1-ast/recno/rec_search.c
new file mode 100644
index 000000000..e70fe4c13
--- /dev/null
+++ b/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/main/db1-ast/recno/rec_seq.c b/main/db1-ast/recno/rec_seq.c
new file mode 100644
index 000000000..ca3451ca6
--- /dev/null
+++ b/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/main/db1-ast/recno/rec_utils.c b/main/db1-ast/recno/rec_utils.c
new file mode 100644
index 000000000..ddc309712
--- /dev/null
+++ b/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/main/db1-ast/recno/recno.h b/main/db1-ast/recno/recno.h
new file mode 100644
index 000000000..bec772c2f
--- /dev/null
+++ b/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/main/devicestate.c b/main/devicestate.c
new file mode 100644
index 000000000..f84c87df7
--- /dev/null
+++ b/main/devicestate.c
@@ -0,0 +1,369 @@
+/*
+ * 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 Device state management
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/logger.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/options.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_LIST_ENTRY(devstate_prov) list;
+};
+
+/*! \brief A list of providers */
+static AST_LIST_HEAD_STATIC(devstate_provs, devstate_prov);
+
+/*! \brief A device state watcher (callback) */
+struct devstate_cb {
+ void *data;
+ ast_devstate_cb_type callback;
+ AST_LIST_ENTRY(devstate_cb) list;
+};
+
+/*! \brief A device state watcher list */
+static AST_LIST_HEAD_STATIC(devstate_cbs, devstate_cb);
+
+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;
+
+/* Forward declarations */
+static int getproviderstate(const char *provider, const char *address);
+
+/*! \brief Find devicestate as text message for output */
+const char *devstate2str(int devstate)
+{
+ return devstatestring[devstate];
+}
+
+/*! \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
+*/
+int ast_parse_device_state(const char *device)
+{
+ struct ast_channel *chan;
+ char match[AST_CHANNEL_NAME];
+ int 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;
+}
+
+/*! \brief Check device state through channel specific function or generic function */
+int ast_device_state(const char *device)
+{
+ char *buf;
+ char *number;
+ const struct ast_channel_tech *chan_tech;
+ int res = 0;
+ /*! \brief Channel driver that provides device state */
+ char *tech;
+ /*! \brief Another provider of device state */
+ char *provider = NULL;
+
+ buf = ast_strdupa(device);
+ tech = strsep(&buf, "/");
+ number = buf;
+ if (!number) {
+ provider = strsep(&tech, ":");
+ if (!provider)
+ return AST_DEVICE_INVALID;
+ /* We have a provider */
+ number = tech;
+ tech = NULL;
+ }
+
+ if (provider) {
+ if(option_debug > 2)
+ ast_log(LOG_DEBUG, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
+ return getproviderstate(provider, number);
+ }
+ if (option_debug > 3)
+ ast_log(LOG_DEBUG, "No provider found, checking channel drivers for %s - %s\n", tech, number);
+
+ chan_tech = ast_get_channel_tech(tech);
+ if (!chan_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 */
+ else {
+ res = chan_tech->devicestate(number); /* Ask the channel driver for device state */
+ if (res == AST_DEVICE_UNKNOWN) {
+ res = ast_parse_device_state(device);
+ /* at this point we know the device exists, but the channel driver
+ could not give us a state; if there is no channel state available,
+ it must be 'not in use'
+ */
+ if (res == AST_DEVICE_UNKNOWN)
+ res = AST_DEVICE_NOT_INUSE;
+ return res;
+ } else
+ 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_LIST_LOCK(&devstate_provs);
+ AST_LIST_INSERT_HEAD(&devstate_provs, devprov, list);
+ AST_LIST_UNLOCK(&devstate_provs);
+
+ return 0;
+}
+
+/*! \brief Remove device state provider */
+void ast_devstate_prov_del(const char *label)
+{
+ struct devstate_prov *devcb;
+
+ AST_LIST_LOCK(&devstate_provs);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
+ if (!strcasecmp(devcb->label, label)) {
+ AST_LIST_REMOVE_CURRENT(&devstate_provs, list);
+ free(devcb);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&devstate_provs);
+}
+
+/*! \brief Get provider device state */
+static int getproviderstate(const char *provider, const char *address)
+{
+ struct devstate_prov *devprov;
+ int res = AST_DEVICE_INVALID;
+
+
+ AST_LIST_LOCK(&devstate_provs);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devprov, list) {
+ if(option_debug > 4)
+ ast_log(LOG_DEBUG, "Checking provider %s with %s\n", devprov->label, provider);
+
+ if (!strcasecmp(devprov->label, provider)) {
+ res = devprov->callback(address);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&devstate_provs);
+ return res;
+}
+
+/*! \brief Add device state watcher */
+int ast_devstate_add(ast_devstate_cb_type callback, void *data)
+{
+ struct devstate_cb *devcb;
+
+ if (!callback || !(devcb = ast_calloc(1, sizeof(*devcb))))
+ return -1;
+
+ devcb->data = data;
+ devcb->callback = callback;
+
+ AST_LIST_LOCK(&devstate_cbs);
+ AST_LIST_INSERT_HEAD(&devstate_cbs, devcb, list);
+ AST_LIST_UNLOCK(&devstate_cbs);
+
+ return 0;
+}
+
+/*! \brief Remove device state watcher */
+void ast_devstate_del(ast_devstate_cb_type callback, void *data)
+{
+ struct devstate_cb *devcb;
+
+ AST_LIST_LOCK(&devstate_cbs);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs, devcb, list) {
+ if ((devcb->callback == callback) && (devcb->data == data)) {
+ AST_LIST_REMOVE_CURRENT(&devstate_cbs, list);
+ free(devcb);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&devstate_cbs);
+}
+
+/*! \brief Notify callback watchers of change, and notify PBX core for hint updates
+ Normally executed within a separate thread
+*/
+static void do_state_change(const char *device)
+{
+ int state;
+ struct devstate_cb *devcb;
+
+ state = ast_device_state(device);
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
+
+ AST_LIST_LOCK(&devstate_cbs);
+ AST_LIST_TRAVERSE(&devstate_cbs, devcb, list)
+ devcb->callback(device, state, devcb->data);
+ AST_LIST_UNLOCK(&devstate_cbs);
+
+ ast_hint_state_changed(device);
+}
+
+int ast_device_state_changed_literal(const char *device)
+{
+ struct state_change *change;
+
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Notification of state change to be queued on device/channel %s\n", device);
+
+ 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);
+ if (AST_LIST_FIRST(&state_changes) == change)
+ /* the list was empty, signal the thread */
+ ast_cond_signal(&change_pending);
+ AST_LIST_UNLOCK(&state_changes);
+ }
+
+ return 1;
+}
+
+/*! \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_device_state_changed_literal(buf);
+}
+
+/*! \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 *cur;
+
+ AST_LIST_LOCK(&state_changes);
+ for(;;) {
+ /* the list lock will _always_ be held at this point in the loop */
+ cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
+ if (cur) {
+ /* we got an entry, so unlock the list while we process it */
+ AST_LIST_UNLOCK(&state_changes);
+ do_state_change(cur->device);
+ free(cur);
+ AST_LIST_LOCK(&state_changes);
+ } else {
+ /* there was no entry, so atomically unlock the list and wait for
+ the condition to be signalled (returns with the lock held) */
+ ast_cond_wait(&change_pending, &state_changes.lock);
+ }
+ }
+
+ 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/main/dial.c b/main/dial.c
new file mode 100644
index 000000000..401e5af6c
--- /dev/null
+++ b/main/dial.c
@@ -0,0 +1,894 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/dial.h"
+#include "asterisk/pbx.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 */
+ 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(, 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 */
+ const char *tech; /*! Technology being dialed */
+ const 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);
+
+/* Structure for 'ANSWER_EXEC' option */
+struct answer_exec_struct {
+ char app[AST_MAX_APP]; /* Application name */
+ char *args; /* Application arguments */
+};
+
+/* 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;
+}
+
+/* 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)
+ free(answer_exec->args);
+
+ /* This is simple - just free the structure */
+ free(answer_exec);
+
+ return 0;
+}
+
+/* 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_MAX, NULL, NULL }, /*! Terminator of list */
+};
+
+/* 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(&dial->channels);
+
+ /* Initialize thread to NULL */
+ dial->thread = AST_PTHREADT_NULL;
+
+ /* 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 = tech;
+ channel->device = device;
+
+ /* Grab reference number from dial structure */
+ channel->num = ast_atomic_fetchadd_int(&dial->num, +1);
+
+ /* Insert into channels list */
+ AST_LIST_INSERT_TAIL(&dial->channels, channel, list);
+
+ return channel->num;
+}
+
+/*! \brief Helper function that does the beginning dialing */
+static int begin_dial(struct ast_dial *dial, struct ast_channel *chan)
+{
+ struct ast_dial_channel *channel = NULL;
+ int success = 0, res = 0;
+
+ /* Iterate through channel list, requesting and calling each one */
+ AST_LIST_LOCK(&dial->channels);
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ char numsubst[AST_MAX_EXTENSION];
+
+ /* Copy device string over */
+ ast_copy_string(numsubst, channel->device, sizeof(numsubst));
+
+ /* Request that the channel be created */
+ if (!(channel->owner = ast_request(channel->tech,
+ chan ? chan->nativeformats : AST_FORMAT_AUDIO_MASK, numsubst, &channel->cause))) {
+ continue;
+ }
+
+ 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;
+ }
+
+ /* Actually call the device */
+ if ((res = ast_call(channel->owner, numsubst, 0))) {
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ } else {
+ success++;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
+ }
+ }
+ AST_LIST_UNLOCK(&dial->channels);
+
+ /* If number of failures matches the number of channels, then this truly failed */
+ return success;
+}
+
+/*! \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_LOCK(&dial->channels);
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->owner == owner)
+ break;
+ }
+ AST_LIST_UNLOCK(&dial->channels);
+
+ 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:
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", channel->owner->name, chan->name);
+ AST_LIST_LOCK(&dial->channels);
+ AST_LIST_REMOVE(&dial->channels, channel, list);
+ AST_LIST_INSERT_HEAD(&dial->channels, channel, list);
+ AST_LIST_UNLOCK(&dial->channels);
+ set_state(dial, AST_DIAL_RESULT_ANSWERED);
+ break;
+ case AST_CONTROL_BUSY:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s is busy\n", channel->owner->name);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ break;
+ case AST_CONTROL_CONGESTION:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s is circuit-busy\n", channel->owner->name);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ break;
+ case AST_CONTROL_RINGING:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s is ringing\n", channel->owner->name);
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ set_state(dial, AST_DIAL_RESULT_RINGING);
+ break;
+ case AST_CONTROL_PROGRESS:
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_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:
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_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_SRCUPDATE:
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_3 "%s requested a source update, passing it to %s\n", channel->owner->name, chan->name);
+ ast_indicate(chan, AST_CONTROL_SRCUPDATE);
+ break;
+ case AST_CONTROL_PROCEEDING:
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_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:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Call on %s placed on hold\n", chan->name);
+ ast_indicate(chan, AST_CONTROL_HOLD);
+ break;
+ case AST_CONTROL_UNHOLD:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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:
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s answered\n", channel->owner->name);
+ AST_LIST_LOCK(&dial->channels);
+ AST_LIST_REMOVE(&dial->channels, channel, list);
+ AST_LIST_INSERT_HEAD(&dial->channels, channel, list);
+ AST_LIST_UNLOCK(&dial->channels);
+ set_state(dial, AST_DIAL_RESULT_ANSWERED);
+ break;
+ case AST_CONTROL_BUSY:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s is busy\n", channel->owner->name);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ break;
+ case AST_CONTROL_CONGESTION:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s is circuit-busy\n", channel->owner->name);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ break;
+ case AST_CONTROL_RINGING:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s is ringing\n", channel->owner->name);
+ set_state(dial, AST_DIAL_RESULT_RINGING);
+ break;
+ case AST_CONTROL_PROGRESS:
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_3 "%s is making progress\n", channel->owner->name);
+ set_state(dial, AST_DIAL_RESULT_PROGRESS);
+ break;
+ case AST_CONTROL_PROCEEDING:
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_3 "%s is proceeding\n", channel->owner->name);
+ set_state(dial, AST_DIAL_RESULT_PROCEEDING);
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+/*! \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, count = 0;
+ struct ast_channel *cs[AST_MAX_WATCHERS], *who = NULL;
+ struct ast_dial_channel *channel = NULL;
+ struct answer_exec_struct *answer_exec = NULL;
+
+ 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);
+ }
+
+ /* 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;
+ 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_LOCK(&dial->channels);
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->owner) {
+ cs[pos++] = channel->owner;
+ count++;
+ }
+ }
+ AST_LIST_UNLOCK(&dial->channels);
+
+ /* 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 we are not being cancelled and we have no channel, then timeout was tripped */
+ if (!who)
+ continue;
+
+ /* Find relative dial channel */
+ if (!chan || !IS_CALLER(chan, who))
+ channel = find_relative_dial_channel(dial, who);
+
+ /* 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;
+ }
+ 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_LOCK(&dial->channels);
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (!channel->owner || channel->owner == who)
+ continue;
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ }
+ AST_LIST_UNLOCK(&dial->channels);
+ /* 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;
+ }
+ } else if (dial->state == AST_DIAL_RESULT_HANGUP) {
+ /* Hangup everything */
+ AST_LIST_LOCK(&dial->channels);
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (!channel->owner)
+ continue;
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ }
+ AST_LIST_UNLOCK(&dial->channels);
+ }
+
+ 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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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 */
+ AST_LIST_LOCK(&dial->channels);
+ if (AST_LIST_FIRST(&dial->channels)->is_running_app) {
+ struct ast_channel *chan = AST_LIST_FIRST(&dial->channels)->owner;
+ if (chan) {
+ 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);
+ }
+ AST_LIST_UNLOCK(&dial->channels);
+
+ /* 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_LOCK(&dial->channels);
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->owner) {
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ }
+ }
+ AST_LIST_UNLOCK(&dial->channels);
+
+ 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_LOCK(&dial->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_LIST_REMOVE_CURRENT(&dial->channels, list);
+ free(channel);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&dial->channels);
+
+ /* 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 */
+ 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 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;
+
+ /* Look for channel, we can sort of cheat and predict things - the last channel in the list will probably be what they want */
+ AST_LIST_LOCK(&dial->channels);
+ if (AST_LIST_LAST(&dial->channels)->num != num) {
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->num == num)
+ break;
+ }
+ } else {
+ channel = AST_LIST_LAST(&dial->channels);
+ }
+ AST_LIST_UNLOCK(&dial->channels);
+
+ /* If none found, return failure */
+ if (!channel)
+ 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;
+
+ /* Look for channel, we can sort of cheat and predict things - the last channel in the list will probably be what they want */
+ AST_LIST_LOCK(&dial->channels);
+ if (AST_LIST_LAST(&dial->channels)->num != num) {
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->num == num)
+ break;
+ }
+ } else {
+ channel = AST_LIST_LAST(&dial->channels);
+ }
+ AST_LIST_UNLOCK(&dial->channels);
+
+ /* If none found, return failure */
+ if (!channel)
+ 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;
+}
diff --git a/main/dns.c b/main/dns.c
new file mode 100644
index 000000000..dc0d3c818
--- /dev/null
+++ b/main/dns.c
@@ -0,0 +1,292 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <unistd.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/dns.h"
+#include "asterisk/endian.h"
+
+#define MAX_SIZE 4096
+
+/* 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 __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 __BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __PDP_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
+ memset(&dnsstate, 0, sizeof(dnsstate));
+ 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_log(LOG_DEBUG, "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/main/dnsmgr.c b/main/dnsmgr.c
new file mode 100644
index 000000000..deb5c73db
--- /dev/null
+++ b/main/dnsmgr.c
@@ -0,0 +1,424 @@
+/*
+ * 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 <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.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/logger.h"
+#include "asterisk/sched.h"
+#include "asterisk/options.h"
+#include "asterisk/cli.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_LIST_ENTRY(ast_dnsmgr_entry) list;
+ /*! just 1 here, but we use calloc to allocate the correct size */
+ char name[1];
+};
+
+static AST_LIST_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_LIST_LOCK(&entry_list);
+ AST_LIST_INSERT_HEAD(&entry_list, entry, list);
+ AST_LIST_UNLOCK(&entry_list);
+
+ return entry;
+}
+
+void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry)
+{
+ if (!entry)
+ return;
+
+ AST_LIST_LOCK(&entry_list);
+ AST_LIST_REMOVE(&entry_list, entry, list);
+ AST_LIST_UNLOCK(&entry_list);
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_4 "removing dns manager for '%s'\n", entry->name);
+
+ ast_mutex_destroy(&entry->lock);
+ 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;
+
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_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;
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_2 "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 && (option_verbose > 2))
+ ast_verbose(VERBOSE_PREFIX_2 "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;
+ }
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_2 "Refreshing DNS lookups.\n");
+ AST_LIST_LOCK(info->entries);
+ AST_LIST_TRAVERSE(info->entries, entry, list) {
+ if (info->regex_present && regexec(&info->filter, entry->name, 0, NULL, 0))
+ continue;
+
+ dnsmgr_refresh(entry, info->verbose);
+ }
+ AST_LIST_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 int handle_cli_reload(int fd, int argc, char *argv[])
+{
+ if (argc > 2)
+ return RESULT_SHOWUSAGE;
+
+ do_reload(0);
+ return 0;
+}
+
+static int handle_cli_refresh(int fd, int argc, char *argv[])
+{
+ struct refresh_info info = {
+ .entries = &entry_list,
+ .verbose = 1,
+ };
+
+ if(!enabled) {
+ ast_cli(fd, "DNS Manager is disabled.\n");
+ return 0;
+ }
+
+ if (argc > 3)
+ return RESULT_SHOWUSAGE;
+
+ if (argc == 3) {
+ if (regcomp(&info.filter, argv[2], REG_EXTENDED | REG_NOSUB))
+ return RESULT_SHOWUSAGE;
+ else
+ info.regex_present = 1;
+ }
+
+ refresh_list(&info);
+
+ if (info.regex_present)
+ regfree(&info.filter);
+
+ return 0;
+}
+
+static int handle_cli_status(int fd, int argc, char *argv[])
+{
+ int count = 0;
+ struct ast_dnsmgr_entry *entry;
+
+ if (argc > 2)
+ return RESULT_SHOWUSAGE;
+
+ ast_cli(fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled");
+ ast_cli(fd, "Refresh Interval: %d seconds\n", refresh_interval);
+ AST_LIST_LOCK(&entry_list);
+ AST_LIST_TRAVERSE(&entry_list, entry, list)
+ count++;
+ AST_LIST_UNLOCK(&entry_list);
+ ast_cli(fd, "Number of entries: %d\n", count);
+
+ return 0;
+}
+
+static struct ast_cli_entry cli_reload = {
+ { "dnsmgr", "reload", NULL },
+ handle_cli_reload, "Reloads the DNS manager configuration",
+ "Usage: dnsmgr reload\n"
+ " Reloads the DNS manager configuration.\n"
+};
+
+static struct ast_cli_entry cli_refresh = {
+ { "dnsmgr", "refresh", NULL },
+ handle_cli_refresh, "Performs an immediate refresh",
+ "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",
+};
+
+static struct ast_cli_entry cli_status = {
+ { "dnsmgr", "status", NULL },
+ handle_cli_status, "Display the DNS manager status",
+ "Usage: dnsmgr status\n"
+ " Displays the DNS manager status.\n"
+};
+
+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;
+ const char *interval_value;
+ const char *enabled_value;
+ int interval;
+ int was_enabled;
+ int res = -1;
+
+ /* 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;
+
+ AST_SCHED_DEL(sched, refresh_sched);
+
+ if ((config = ast_config_load("dnsmgr.conf"))) {
+ 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;
+ res = 0;
+ }
+ else
+ res = 0;
+
+ ast_mutex_unlock(&refresh_lock);
+
+ return res;
+}
diff --git a/main/dsp.c b/main/dsp.c
new file mode 100644
index 000000000..3caf65027
--- /dev/null
+++ b/main/dsp.c
@@ -0,0 +1,1819 @@
+/*
+ * 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 <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/logger.h"
+#include "asterisk/dsp.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/alaw.h"
+#include "asterisk/utils.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 if you want the fax detector -- NOT RECOMMENDED IN -STABLE */
+#define FAX_DETECT
+
+#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 */
+
+#ifdef RADIO_RELAX
+#define DTMF_NORMAL_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 11.3 : 6.3) /* 8dB sph 12.3 was 6.3 */
+#define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 9.5 : 2.5) /* 4dB normal sph 12.5 : 5.5 was 6.5 : 2.5 */
+#define DTMF_RELATIVE_PEAK_ROW ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 3.3 : 6.3) /* 8dB sph was 6.3 */
+#define DTMF_RELATIVE_PEAK_COL ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 3.3 : 6.3) /* 8dB sph was 6.3 */
+#define DTMF_TO_TOTAL_ENERGY ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 26.0 : 42.0)
+#else
+#define DTMF_NORMAL_TWIST 6.3
+#define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 4.0 : 2.5) /* 4dB normal */
+#define DTMF_RELATIVE_PEAK_ROW 6.3 /* 8dB */
+#define DTMF_RELATIVE_PEAK_COL 6.3 /* 8dB */
+#define DTMF_TO_TOTAL_ENERGY 42.0
+#endif
+
+#ifdef OLD_DSP_ROUTINES
+#define DTMF_2ND_HARMONIC_ROW ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 1.7 : 2.5) /* 4dB normal */
+#define DTMF_2ND_HARMONIC_COL 63.1 /* 18dB */
+
+#define MF_THRESHOLD 8.0e7
+#define MF_NORMAL_TWIST 5.3 /* 8dB */
+#define MF_REVERSE_TWIST 4.0 /* was 2.5 */
+#define MF_RELATIVE_PEAK 5.3 /* 8dB */
+#define MF_2ND_HARMONIC 1.7 /* was 2.5 */
+#else
+#define BELL_MF_THRESHOLD 1.6e9
+#define BELL_MF_TWIST 4.0 /* 6dB */
+#define BELL_MF_RELATIVE_PEAK 12.6 /* 11dB */
+#endif
+
+#if !defined(BUSYDETECT_MARTIN) && !defined(BUSYDETECT) && !defined(BUSYDETECT_TONEONLY) && !defined(BUSYDETECT_COMPARE_TONE_AND_SILENCE)
+#define BUSYDETECT_MARTIN
+#endif
+
+typedef struct {
+ float v2;
+ float v3;
+ float fac;
+#ifndef OLD_DSP_ROUTINES
+ int samples;
+#endif
+} goertzel_state_t;
+
+typedef struct
+{
+ goertzel_state_t row_out[4];
+ goertzel_state_t col_out[4];
+#ifdef FAX_DETECT
+ goertzel_state_t fax_tone;
+#endif
+#ifdef OLD_DSP_ROUTINES
+ goertzel_state_t row_out2nd[4];
+ goertzel_state_t col_out2nd[4];
+#ifdef FAX_DETECT
+ goertzel_state_t fax_tone2nd;
+#endif
+ int hit1;
+ int hit2;
+ int hit3;
+ int hit4;
+#else
+ int lasthit;
+#endif
+ 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];
+#ifdef FAX_DETECT
+ int fax_hits;
+#endif
+} dtmf_detect_state_t;
+
+typedef struct
+{
+ goertzel_state_t tone_out[6];
+ int mhit;
+#ifdef OLD_DSP_ROUTINES
+ int hit1;
+ int hit2;
+ int hit3;
+ int hit4;
+ goertzel_state_t tone_out2nd[6];
+ float energy;
+#else
+ int hits[5];
+#endif
+ int current_sample;
+
+ char digits[MAX_DTMF_DIGITS + 1];
+
+ int current_digits;
+ int detected_digits;
+ int lost_digits;
+#ifdef FAX_DETECT
+ int fax_hits;
+#endif
+} 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
+};
+
+#ifdef FAX_DETECT
+static float fax_freq = 1100.0;
+#endif
+
+static char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
+
+#ifdef OLD_DSP_ROUTINES
+static char mf_hit[6][6] = {
+ /* 700 + */ { 0, '1', '2', '4', '7', 'C' },
+ /* 900 + */ { '1', 0, '3', '5', '8', 'A' },
+ /* 1100 + */ { '2', '3', 0, '6', '9', '*' },
+ /* 1300 + */ { '4', '5', '6', 0, '0', 'B' },
+ /* 1500 + */ { '7', '8', '9', '0', 0, '#' },
+ /* 1700 + */ { 'C', 'A', '*', 'B', '#', 0 },
+};
+#else
+static char bell_mf_positions[] = "1247C-358A--69*---0B----#";
+#endif
+
+static inline void goertzel_sample(goertzel_state_t *s, short sample)
+{
+ float v1;
+ float fsamp = sample;
+
+ v1 = s->v2;
+ s->v2 = s->v3;
+ s->v3 = s->fac * s->v2 - v1 + fsamp;
+}
+
+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)
+{
+ return s->v3 * s->v3 + s->v2 * s->v2 - s->v2 * s->v3 * s->fac;
+}
+
+static inline void goertzel_init(goertzel_state_t *s, float freq, int samples)
+{
+ s->v2 = s->v3 = 0.0;
+ s->fac = 2.0 * cos(2.0 * M_PI * (freq / 8000.0));
+#ifndef OLD_DSP_ROUTINES
+ s->samples = samples;
+#endif
+}
+
+static inline void goertzel_reset(goertzel_state_t *s)
+{
+ s->v2 = s->v3 = 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;
+
+#ifdef OLD_DSP_ROUTINES
+ s->hit1 =
+ s->mhit =
+ s->hit3 =
+ s->hit4 =
+ s->hit2 = 0;
+#else
+ s->lasthit = 0;
+#endif
+ 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);
+#ifdef OLD_DSP_ROUTINES
+ goertzel_init (&s->row_out2nd[i], dtmf_row[i] * 2.0, 102);
+ goertzel_init (&s->col_out2nd[i], dtmf_col[i] * 2.0, 102);
+#endif
+ s->energy = 0.0;
+ }
+#ifdef FAX_DETECT
+ /* Same for the fax dector */
+ goertzel_init (&s->fax_tone, fax_freq, 102);
+
+#ifdef OLD_DSP_ROUTINES
+ /* Same for the fax dector 2nd harmonic */
+ goertzel_init (&s->fax_tone2nd, fax_freq * 2.0, 102);
+#endif
+#endif /* FAX_DETECT */
+ 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;
+#ifdef OLD_DSP_ROUTINES
+ s->hit1 =
+ s->hit2 = 0;
+#else
+ s->hits[0] = s->hits[1] = s->hits[2] = s->hits[3] = s->hits[4] = 0;
+#endif
+ for (i = 0; i < 6; i++) {
+ goertzel_init (&s->tone_out[i], mf_tones[i], 160);
+#ifdef OLD_DSP_ROUTINES
+ goertzel_init (&s->tone_out2nd[i], mf_tones[i] * 2.0, 160);
+ s->energy = 0.0;
+#endif
+ }
+ 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];
+#ifdef FAX_DETECT
+ float fax_energy;
+#ifdef OLD_DSP_ROUTINES
+ float fax_energy_2nd;
+#endif
+#endif /* FAX_DETECT */
+ float famp;
+ float v1;
+ 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;
+#if defined(USE_3DNOW)
+ _dtmf_goertzel_update (s->row_out, amp + sample, limit - sample);
+ _dtmf_goertzel_update (s->col_out, amp + sample, limit - sample);
+#ifdef OLD_DSP_ROUTINES
+ _dtmf_goertzel_update (s->row_out2nd, amp + sample, limit2 - sample);
+ _dtmf_goertzel_update (s->col_out2nd, amp + sample, limit2 - sample);
+#endif
+ /* XXX Need to fax detect for 3dnow too XXX */
+ #warning "Fax Support Broken"
+#else
+ /* 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 */
+ v1 = s->row_out[0].v2;
+ s->row_out[0].v2 = s->row_out[0].v3;
+ s->row_out[0].v3 = s->row_out[0].fac*s->row_out[0].v2 - v1 + famp;
+ v1 = s->col_out[0].v2;
+ s->col_out[0].v2 = s->col_out[0].v3;
+ s->col_out[0].v3 = s->col_out[0].fac*s->col_out[0].v2 - v1 + famp;
+ v1 = s->row_out[1].v2;
+ s->row_out[1].v2 = s->row_out[1].v3;
+ s->row_out[1].v3 = s->row_out[1].fac*s->row_out[1].v2 - v1 + famp;
+ v1 = s->col_out[1].v2;
+ s->col_out[1].v2 = s->col_out[1].v3;
+ s->col_out[1].v3 = s->col_out[1].fac*s->col_out[1].v2 - v1 + famp;
+ v1 = s->row_out[2].v2;
+ s->row_out[2].v2 = s->row_out[2].v3;
+ s->row_out[2].v3 = s->row_out[2].fac*s->row_out[2].v2 - v1 + famp;
+ v1 = s->col_out[2].v2;
+ s->col_out[2].v2 = s->col_out[2].v3;
+ s->col_out[2].v3 = s->col_out[2].fac*s->col_out[2].v2 - v1 + famp;
+ v1 = s->row_out[3].v2;
+ s->row_out[3].v2 = s->row_out[3].v3;
+ s->row_out[3].v3 = s->row_out[3].fac*s->row_out[3].v2 - v1 + famp;
+ v1 = s->col_out[3].v2;
+ s->col_out[3].v2 = s->col_out[3].v3;
+ s->col_out[3].v3 = s->col_out[3].fac*s->col_out[3].v2 - v1 + famp;
+#ifdef FAX_DETECT
+ /* Update fax tone */
+ v1 = s->fax_tone.v2;
+ s->fax_tone.v2 = s->fax_tone.v3;
+ s->fax_tone.v3 = s->fax_tone.fac*s->fax_tone.v2 - v1 + famp;
+#endif /* FAX_DETECT */
+#ifdef OLD_DSP_ROUTINES
+ v1 = s->col_out2nd[0].v2;
+ s->col_out2nd[0].v2 = s->col_out2nd[0].v3;
+ s->col_out2nd[0].v3 = s->col_out2nd[0].fac*s->col_out2nd[0].v2 - v1 + famp;
+ v1 = s->row_out2nd[0].v2;
+ s->row_out2nd[0].v2 = s->row_out2nd[0].v3;
+ s->row_out2nd[0].v3 = s->row_out2nd[0].fac*s->row_out2nd[0].v2 - v1 + famp;
+ v1 = s->col_out2nd[1].v2;
+ s->col_out2nd[1].v2 = s->col_out2nd[1].v3;
+ s->col_out2nd[1].v3 = s->col_out2nd[1].fac*s->col_out2nd[1].v2 - v1 + famp;
+ v1 = s->row_out2nd[1].v2;
+ s->row_out2nd[1].v2 = s->row_out2nd[1].v3;
+ s->row_out2nd[1].v3 = s->row_out2nd[1].fac*s->row_out2nd[1].v2 - v1 + famp;
+ v1 = s->col_out2nd[2].v2;
+ s->col_out2nd[2].v2 = s->col_out2nd[2].v3;
+ s->col_out2nd[2].v3 = s->col_out2nd[2].fac*s->col_out2nd[2].v2 - v1 + famp;
+ v1 = s->row_out2nd[2].v2;
+ s->row_out2nd[2].v2 = s->row_out2nd[2].v3;
+ s->row_out2nd[2].v3 = s->row_out2nd[2].fac*s->row_out2nd[2].v2 - v1 + famp;
+ v1 = s->col_out2nd[3].v2;
+ s->col_out2nd[3].v2 = s->col_out2nd[3].v3;
+ s->col_out2nd[3].v3 = s->col_out2nd[3].fac*s->col_out2nd[3].v2 - v1 + famp;
+ v1 = s->row_out2nd[3].v2;
+ s->row_out2nd[3].v2 = s->row_out2nd[3].v3;
+ s->row_out2nd[3].v3 = s->row_out2nd[3].fac*s->row_out2nd[3].v2 - v1 + famp;
+#ifdef FAX_DETECT
+ /* Update fax tone */
+ v1 = s->fax_tone.v2;
+ s->fax_tone2nd.v2 = s->fax_tone2nd.v3;
+ s->fax_tone2nd.v3 = s->fax_tone2nd.fac*s->fax_tone2nd.v2 - v1 + famp;
+#endif /* FAX_DETECT */
+#endif
+ }
+#endif
+ 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;
+ }
+#ifdef FAX_DETECT
+ /* Detect the fax energy, too */
+ fax_energy = goertzel_result(&s->fax_tone);
+#endif
+ /* 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;
+ }
+ }
+#ifdef OLD_DSP_ROUTINES
+ /* ... and second harmonic test */
+ if (i >= 4 &&
+ (row_energy[best_row] + col_energy[best_col]) > 42.0*s->energy &&
+ goertzel_result(&s->col_out2nd[best_col])*DTMF_2ND_HARMONIC_COL < col_energy[best_col]
+ && goertzel_result(&s->row_out2nd[best_row])*DTMF_2ND_HARMONIC_ROW < row_energy[best_row]) {
+#else
+ /* ... and fraction of total energy test */
+ if (i >= 4 &&
+ (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY*s->energy) {
+#endif
+ /* 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;
+ }
+#ifdef OLD_DSP_ROUTINES
+ /* 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->hit3 && s->hit3 != s->hit2) {
+ s->mhit = 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++;
+ }
+ }
+#endif
+ }
+ }
+
+#ifndef OLD_DSP_ROUTINES
+ /* 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;
+ }
+#endif
+
+#ifdef FAX_DETECT
+ if (!hit && (fax_energy >= FAX_THRESHOLD) &&
+ (fax_energy >= DTMF_TO_TOTAL_ENERGY*s->energy) &&
+ (faxdetect)) {
+#if 0
+ printf("Fax energy/Second Harmonic: %f\n", fax_energy);
+#endif
+ /* 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;
+ }
+#endif /* FAX_DETECT */
+#ifdef OLD_DSP_ROUTINES
+ s->hit1 = s->hit2;
+ s->hit2 = s->hit3;
+ s->hit3 = hit;
+#else
+ s->lasthit = hit;
+#endif
+ /* 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]);
+#ifdef OLD_DSP_ROUTINES
+ goertzel_reset(&s->row_out2nd[i]);
+ goertzel_reset(&s->col_out2nd[i]);
+#endif
+ }
+#ifdef FAX_DETECT
+ goertzel_reset (&s->fax_tone);
+#ifdef OLD_DSP_ROUTINES
+ goertzel_reset (&s->fax_tone2nd);
+#endif
+#endif
+ s->energy = 0.0;
+ s->current_sample = 0;
+ }
+#ifdef OLD_DSP_ROUTINES
+ if ((!s->mhit) || (s->mhit != hit)) {
+ s->mhit = 0;
+ return(0);
+ }
+ return (hit);
+#else
+ return (s->mhit); /* return the debounced hit */
+#endif
+}
+
+/* MF goertzel size */
+#ifdef OLD_DSP_ROUTINES
+#define MF_GSIZE 160
+#else
+#define MF_GSIZE 120
+#endif
+
+static int mf_detect (mf_detect_state_t *s, int16_t amp[],
+ int samples, int digitmode, int *writeback)
+{
+#ifdef OLD_DSP_ROUTINES
+ float tone_energy[6];
+ int best1;
+ int best2;
+ float max;
+ int sofarsogood;
+#else
+ float energy[6];
+ int best;
+ int second_best;
+#endif
+ float famp;
+ float v1;
+ 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;
+#if defined(USE_3DNOW)
+ _dtmf_goertzel_update (s->row_out, amp + sample, limit - sample);
+ _dtmf_goertzel_update (s->col_out, amp + sample, limit - sample);
+#ifdef OLD_DSP_ROUTINES
+ _dtmf_goertzel_update (s->row_out2nd, amp + sample, limit2 - sample);
+ _dtmf_goertzel_update (s->col_out2nd, amp + sample, limit2 - sample);
+#endif
+ /* XXX Need to fax detect for 3dnow too XXX */
+ #warning "Fax Support Broken"
+#else
+ /* 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];
+#ifdef OLD_DSP_ROUTINES
+ s->energy += famp*famp;
+#endif
+ /* With GCC 2.95, the following unrolled code seems to take about 35%
+ (rough estimate) as long as a neat little 0-3 loop */
+ v1 = s->tone_out[0].v2;
+ s->tone_out[0].v2 = s->tone_out[0].v3;
+ s->tone_out[0].v3 = s->tone_out[0].fac*s->tone_out[0].v2 - v1 + famp;
+ v1 = s->tone_out[1].v2;
+ s->tone_out[1].v2 = s->tone_out[1].v3;
+ s->tone_out[1].v3 = s->tone_out[1].fac*s->tone_out[1].v2 - v1 + famp;
+ v1 = s->tone_out[2].v2;
+ s->tone_out[2].v2 = s->tone_out[2].v3;
+ s->tone_out[2].v3 = s->tone_out[2].fac*s->tone_out[2].v2 - v1 + famp;
+ v1 = s->tone_out[3].v2;
+ s->tone_out[3].v2 = s->tone_out[3].v3;
+ s->tone_out[3].v3 = s->tone_out[3].fac*s->tone_out[3].v2 - v1 + famp;
+ v1 = s->tone_out[4].v2;
+ s->tone_out[4].v2 = s->tone_out[4].v3;
+ s->tone_out[4].v3 = s->tone_out[4].fac*s->tone_out[4].v2 - v1 + famp;
+ v1 = s->tone_out[5].v2;
+ s->tone_out[5].v2 = s->tone_out[5].v3;
+ s->tone_out[5].v3 = s->tone_out[5].fac*s->tone_out[5].v2 - v1 + famp;
+#ifdef OLD_DSP_ROUTINES
+ v1 = s->tone_out2nd[0].v2;
+ s->tone_out2nd[0].v2 = s->tone_out2nd[0].v3;
+ s->tone_out2nd[0].v3 = s->tone_out2nd[0].fac*s->tone_out2nd[0].v2 - v1 + famp;
+ v1 = s->tone_out2nd[1].v2;
+ s->tone_out2nd[1].v2 = s->tone_out2nd[1].v3;
+ s->tone_out2nd[1].v3 = s->tone_out2nd[1].fac*s->tone_out2nd[1].v2 - v1 + famp;
+ v1 = s->tone_out2nd[2].v2;
+ s->tone_out2nd[2].v2 = s->tone_out2nd[2].v3;
+ s->tone_out2nd[2].v3 = s->tone_out2nd[2].fac*s->tone_out2nd[2].v2 - v1 + famp;
+ v1 = s->tone_out2nd[3].v2;
+ s->tone_out2nd[3].v2 = s->tone_out2nd[3].v3;
+ s->tone_out2nd[3].v3 = s->tone_out2nd[3].fac*s->tone_out2nd[3].v2 - v1 + famp;
+ v1 = s->tone_out2nd[4].v2;
+ s->tone_out2nd[4].v2 = s->tone_out2nd[4].v3;
+ s->tone_out2nd[4].v3 = s->tone_out2nd[4].fac*s->tone_out2nd[2].v2 - v1 + famp;
+ v1 = s->tone_out2nd[3].v2;
+ s->tone_out2nd[5].v2 = s->tone_out2nd[6].v3;
+ s->tone_out2nd[5].v3 = s->tone_out2nd[6].fac*s->tone_out2nd[3].v2 - v1 + famp;
+#endif
+ }
+#endif
+ 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;
+ }
+#ifdef OLD_DSP_ROUTINES
+ /* We're at the end of an MF detection block. Go ahead and calculate
+ all the energies. */
+ for (i=0;i<6;i++) {
+ tone_energy[i] = goertzel_result(&s->tone_out[i]);
+ }
+ /* Find highest */
+ best1 = 0;
+ max = tone_energy[0];
+ for (i=1;i<6;i++) {
+ if (tone_energy[i] > max) {
+ max = tone_energy[i];
+ best1 = i;
+ }
+ }
+
+ /* Find 2nd highest */
+ if (best1) {
+ max = tone_energy[0];
+ best2 = 0;
+ } else {
+ max = tone_energy[1];
+ best2 = 1;
+ }
+
+ for (i=0;i<6;i++) {
+ if (i == best1) continue;
+ if (tone_energy[i] > max) {
+ max = tone_energy[i];
+ best2 = i;
+ }
+ }
+ hit = 0;
+ if (best1 != best2)
+ sofarsogood=1;
+ else
+ sofarsogood=0;
+ /* Check for relative energies */
+ for (i=0;i<6;i++) {
+ if (i == best1)
+ continue;
+ if (i == best2)
+ continue;
+ if (tone_energy[best1] < tone_energy[i] * MF_RELATIVE_PEAK) {
+ sofarsogood = 0;
+ break;
+ }
+ if (tone_energy[best2] < tone_energy[i] * MF_RELATIVE_PEAK) {
+ sofarsogood = 0;
+ break;
+ }
+ }
+
+ if (sofarsogood) {
+ /* Check for 2nd harmonic */
+ if (goertzel_result(&s->tone_out2nd[best1]) * MF_2ND_HARMONIC > tone_energy[best1])
+ sofarsogood = 0;
+ else if (goertzel_result(&s->tone_out2nd[best2]) * MF_2ND_HARMONIC > tone_energy[best2])
+ sofarsogood = 0;
+ }
+ if (sofarsogood) {
+ hit = mf_hit[best1][best2];
+ 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 consecutive clean hits */
+ if ((hit == s->hit3) && (s->hit3 != s->hit2)) {
+ s->mhit = hit;
+ s->detected_digits++;
+ if (s->current_digits < MAX_DTMF_DIGITS - 2) {
+ s->digits[s->current_digits++] = hit;
+ s->digits[s->current_digits] = '\0';
+ } else {
+ s->lost_digits++;
+ }
+ }
+ }
+
+ s->hit1 = s->hit2;
+ s->hit2 = s->hit3;
+ s->hit3 = hit;
+ /* Reinitialise the detector for the next block */
+ for (i = 0; i < 6; i++) {
+ goertzel_reset(&s->tone_out[i]);
+ goertzel_reset(&s->tone_out2nd[i]);
+ }
+ s->energy = 0.0;
+ s->current_sample = 0;
+ }
+#else
+ /* 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;
+ }
+#endif
+ 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]);
+#if 0
+ printf("\n350: 425: 440: 480: 620: 950: 1400: 1800: Energy: \n");
+ printf("%.2e %.2e %.2e %.2e %.2e %.2e %.2e %.2e %.2e\n",
+ hz[HZ_350], hz[HZ_425], hz[HZ_440], hz[HZ_480], hz[HZ_620], hz[HZ_950], hz[HZ_1400], hz[HZ_1800], dsp->genergy);
+#endif
+ 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) {
+#if 0
+ ast_log(LOG_NOTICE, "Consider call as answered because of timeout after last ring\n");
+#endif
+ res = AST_CONTROL_ANSWER;
+ dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
+ }
+ } else {
+#if 0
+ ast_log(LOG_NOTICE, "Stop state %d with duration %d\n", dsp->tstate, dsp->tcount);
+ ast_log(LOG_NOTICE, "Start state %d\n", newstate);
+#endif
+ 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;
+ }
+ }
+#if 0
+ if (res)
+ printf("Returning %d\n", res);
+#endif
+ 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;
+}
+
+#ifdef BUSYDETECT_MARTIN
+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
+#ifdef BUSYDETECT_TONEONLY
+#error You cant use BUSYDETECT_TONEONLY together with BUSYDETECT_COMPARE_TONE_AND_SILENCE
+#endif
+ 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)) {
+#if 0
+ ast_log(LOG_NOTICE, "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)) {
+#if 0
+ ast_log(LOG_NOTICE, "busy detector: avgsilence of %d not close enough to desired %d\n",
+ avgsilence, dsp->busy_quietlength);
+#endif
+ res = 0;
+ }
+ }
+#endif
+#ifndef BUSYDETECT_TONEONLY
+#if 1
+ if (res)
+ ast_log(LOG_DEBUG, "ast_dsp_busydetect detected busy, avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
+#endif
+#endif
+ return res;
+}
+#endif
+
+#ifdef BUSYDETECT
+int ast_dsp_busydetect(struct ast_dsp *dsp)
+{
+ int x;
+ int res = 0;
+ int max, min;
+
+#if 0
+ if (dsp->busy_hits > 5);
+ return 0;
+#endif
+ if (dsp->busymaybe) {
+#if 0
+ printf("Maybe busy!\n");
+#endif
+ dsp->busymaybe = 0;
+ min = 9999;
+ max = 0;
+ for (x=DSP_HISTORY - dsp->busycount;x<DSP_HISTORY;x++) {
+#if 0
+ printf("Silence: %d, Noise: %d\n", dsp->historicsilence[x], dsp->historicnoise[x]);
+#endif
+ if (dsp->historicsilence[x] < min)
+ min = dsp->historicsilence[x];
+ if (dsp->historicnoise[x] < min)
+ min = dsp->historicnoise[x];
+ if (dsp->historicsilence[x] > max)
+ max = dsp->historicsilence[x];
+ if (dsp->historicnoise[x] > max)
+ max = dsp->historicnoise[x];
+ }
+ if ((max - min < BUSY_THRESHOLD) && (max < BUSY_MAX) && (min > BUSY_MIN)) {
+#if 0
+ printf("Busy!\n");
+#endif
+ res = 1;
+ }
+#if 0
+ printf("Min: %d, max: %d\n", min, max);
+#endif
+ }
+ return res;
+}
+#endif
+
+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);
+ ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP);
+ 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_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP);
+ 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);
+ ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP);
+ 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);
+ }
+ ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP);
+ 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);
+ ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP);
+ 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);
+ ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP);
+ 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);
+ ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP);
+ 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)
+{
+ if (ast_test_flag(&dsp->f, AST_FRFLAG_FROM_DSP)) {
+ /* If this flag is still set, that means that the dsp's destruction
+ * 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. */
+
+ /* Set the magic hint that this has been requested to be destroyed. */
+ dsp->freqcount = -1;
+
+ return;
+ }
+ 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_log(LOG_DEBUG, "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]);
+#ifdef OLD_DSP_ROUTINES
+ goertzel_reset(&dsp->td.mf.tone_out2nd[i]);
+#endif
+ }
+#ifdef OLD_DSP_ROUTINES
+ dsp->td.mf.energy = 0.0;
+ dsp->td.mf.hit1 = dsp->td.mf.hit2 = dsp->td.mf.hit3 = dsp->td.mf.hit4 = dsp->td.mf.mhit = 0;
+#else
+ 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;
+#endif
+ 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]);
+#ifdef OLD_DSP_ROUTINES
+ goertzel_reset(&dsp->td.dtmf.row_out2nd[i]);
+ goertzel_reset(&dsp->td.dtmf.col_out2nd[i]);
+#endif
+ }
+#ifdef FAX_DETECT
+ goertzel_reset (&dsp->td.dtmf.fax_tone);
+#endif
+#ifdef OLD_DSP_ROUTINES
+#ifdef FAX_DETECT
+ goertzel_reset (&dsp->td.dtmf.fax_tone2nd);
+#endif
+ dsp->td.dtmf.hit1 = dsp->td.dtmf.hit2 = dsp->td.dtmf.hit3 = dsp->td.dtmf.hit4 = dsp->td.dtmf.mhit = 0;
+#else
+ dsp->td.dtmf.lasthit = dsp->td.dtmf.mhit = 0;
+#endif
+ 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<sizeof(aliases) / sizeof(aliases[0]);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;
+}
+
+void ast_dsp_frame_freed(struct ast_frame *fr)
+{
+ struct ast_dsp *dsp;
+
+ ast_clear_flag(fr, AST_FRFLAG_FROM_DSP);
+
+ dsp = (struct ast_dsp *) (((char *) fr) - offsetof(struct ast_dsp, f));
+
+ if (dsp->freqcount != -1)
+ return;
+
+ ast_dsp_free(dsp);
+}
diff --git a/main/ecdisa.h b/main/ecdisa.h
new file mode 100644
index 000000000..df6f773a0
--- /dev/null
+++ b/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/main/editline/CHANGES b/main/editline/CHANGES
new file mode 100644
index 000000000..c18b56cdf
--- /dev/null
+++ b/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/main/editline/INSTALL b/main/editline/INSTALL
new file mode 100644
index 000000000..16fb6ffd1
--- /dev/null
+++ b/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/main/editline/Makefile.in b/main/editline/Makefile.in
new file mode 100644
index 000000000..5bb097f48
--- /dev/null
+++ b/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 makelist 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/main/editline/PLATFORMS b/main/editline/PLATFORMS
new file mode 100644
index 000000000..ea7c5bb68
--- /dev/null
+++ b/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/main/editline/README b/main/editline/README
new file mode 100644
index 000000000..49a2a6947
--- /dev/null
+++ b/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/main/editline/TEST/test.c b/main/editline/TEST/test.c
new file mode 100644
index 000000000..3169a2071
--- /dev/null
+++ b/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/main/editline/chared.c b/main/editline/chared.c
new file mode 100644
index 000000000..8eaeb3b54
--- /dev/null
+++ b/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/main/editline/chared.h b/main/editline/chared.h
new file mode 100644
index 000000000..403eca011
--- /dev/null
+++ b/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/main/editline/common.c b/main/editline/common.c
new file mode 100644
index 000000000..c831e79a3
--- /dev/null
+++ b/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/main/editline/config.guess b/main/editline/config.guess
new file mode 100755
index 000000000..a6d8a945f
--- /dev/null
+++ b/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/main/editline/config.h.in b/main/editline/config.h.in
new file mode 100644
index 000000000..151fb226d
--- /dev/null
+++ b/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/main/editline/config.sub b/main/editline/config.sub
new file mode 100755
index 000000000..838237e98
--- /dev/null
+++ b/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/main/editline/configure b/main/editline/configure
new file mode 100755
index 000000000..d570bf679
--- /dev/null
+++ b/main/editline/configure
@@ -0,0 +1,2459 @@
+#! /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
+
+for ac_prog in mawk gawk nawk awk
+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 $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:848: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AWK'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # 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_AWK="$ac_prog"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+AWK="$ac_cv_prog_AWK"
+if test -n "$AWK"; then
+ echo "$ac_t""$AWK" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+test -n "$AWK" && break
+done
+
+
+ac_aux_dir=
+for ac_dir in $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:904: 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:955: checking ABI" >&5
+ cat > conftest.$ac_ext <<EOF
+#line 957 "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:1001: 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:1056: 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:1086: 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:1120: 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 1128 "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:1139: \"$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:1166: 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 1174 "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:1185: \"$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:1212: 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 1220 "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:1231: \"$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:1258: 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 1266 "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:1277: \"$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:1317: 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 1322 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1327: \"$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:1355: 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 1360 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1365: \"$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:1397: 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 1402 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1407: \"$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:1435: 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 1440 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1445: \"$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:1484: 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 1489 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1494: \"$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:1524: 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 1529 "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:1552: \"$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 strlcat
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1579: 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 1584 "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:1607: \"$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/strlcat.c"
+fi
+done
+
+for ac_func in strlcpy
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1635: 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 1640 "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:1663: \"$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/strlcpy.c"
+fi
+done
+
+for ac_func in fgetln
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1691: 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 1696 "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:1719: \"$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:1747: 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 1752 "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:1775: \"$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:1803: 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 1808 "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:1831: \"$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 1858 "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 1877 "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 1896 "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 1915 "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"
+else
+ CFLAGS="$CFLAGS -O"
+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 makelist 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%@AWK@%$AWK%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 makelist"}
+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/main/editline/configure.in b/main/editline/configure.in
new file mode 100644
index 000000000..c1f87b842
--- /dev/null
+++ b/main/editline/configure.in
@@ -0,0 +1,278 @@
+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
+AC_PROG_AWK
+
+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(strlcat, , CCSRCS="$CCSRCS np/strlcat.c")
+AC_CHECK_FUNCS(strlcpy, , CCSRCS="$CCSRCS np/strlcpy.c")
+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 makelist])
+
diff --git a/main/editline/editline.3 b/main/editline/editline.3
new file mode 100644
index 000000000..28f6ddb84
--- /dev/null
+++ b/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/main/editline/editrc.5 b/main/editline/editrc.5
new file mode 100644
index 000000000..ddd12897b
--- /dev/null
+++ b/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/main/editline/el.c b/main/editline/el.c
new file mode 100644
index 000000000..514316fbe
--- /dev/null
+++ b/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/main/editline/el.h b/main/editline/el.h
new file mode 100644
index 000000000..641081e87
--- /dev/null
+++ b/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/main/editline/emacs.c b/main/editline/emacs.c
new file mode 100644
index 000000000..f520d024b
--- /dev/null
+++ b/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/main/editline/hist.c b/main/editline/hist.c
new file mode 100644
index 000000000..11f39ae10
--- /dev/null
+++ b/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/main/editline/hist.h b/main/editline/hist.h
new file mode 100644
index 000000000..5fdccd08e
--- /dev/null
+++ b/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/main/editline/histedit.h b/main/editline/histedit.h
new file mode 100644
index 000000000..e387e3b81
--- /dev/null
+++ b/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/main/editline/history.c b/main/editline/history.c
new file mode 100644
index 000000000..f133d2eb0
--- /dev/null
+++ b/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/main/editline/install-sh b/main/editline/install-sh
new file mode 100755
index 000000000..ebc66913e
--- /dev/null
+++ b/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/main/editline/key.c b/main/editline/key.c
new file mode 100644
index 000000000..0dcdf4191
--- /dev/null
+++ b/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/main/editline/key.h b/main/editline/key.h
new file mode 100644
index 000000000..80d8626b8
--- /dev/null
+++ b/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/main/editline/makelist.in b/main/editline/makelist.in
new file mode 100644
index 000000000..9a44dc75b
--- /dev/null
+++ b/main/editline/makelist.in
@@ -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=@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/main/editline/map.c b/main/editline/map.c
new file mode 100644
index 000000000..4187cb597
--- /dev/null
+++ b/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/main/editline/map.h b/main/editline/map.h
new file mode 100644
index 000000000..3c9948ccf
--- /dev/null
+++ b/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/main/editline/np/fgetln.c b/main/editline/np/fgetln.c
new file mode 100644
index 000000000..93da9914d
--- /dev/null
+++ b/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/main/editline/np/strlcat.c b/main/editline/np/strlcat.c
new file mode 100644
index 000000000..6c9f1e92d
--- /dev/null
+++ b/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/main/editline/np/strlcpy.c b/main/editline/np/strlcpy.c
new file mode 100644
index 000000000..1f154bcf2
--- /dev/null
+++ b/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/main/editline/np/unvis.c b/main/editline/np/unvis.c
new file mode 100644
index 000000000..f43c4c749
--- /dev/null
+++ b/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/main/editline/np/vis.c b/main/editline/np/vis.c
new file mode 100644
index 000000000..bd8192440
--- /dev/null
+++ b/main/editline/np/vis.c
@@ -0,0 +1,348 @@
+/* $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>
+#include <stdint.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)(((uint32_t)(u_char)c >> 6) & 03) + '0'; \
+ *dst++ = (u_char)(((uint32_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/main/editline/np/vis.h b/main/editline/np/vis.h
new file mode 100644
index 000000000..0739c1dfa
--- /dev/null
+++ b/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/main/editline/parse.c b/main/editline/parse.c
new file mode 100644
index 000000000..0a34f0b15
--- /dev/null
+++ b/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/main/editline/parse.h b/main/editline/parse.h
new file mode 100644
index 000000000..4aaef2f83
--- /dev/null
+++ b/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/main/editline/prompt.c b/main/editline/prompt.c
new file mode 100644
index 000000000..5c069e17a
--- /dev/null
+++ b/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/main/editline/prompt.h b/main/editline/prompt.h
new file mode 100644
index 000000000..08810e22a
--- /dev/null
+++ b/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/main/editline/read.c b/main/editline/read.c
new file mode 100644
index 000000000..80d51da4a
--- /dev/null
+++ b/main/editline/read.c
@@ -0,0 +1,558 @@
+/* $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.
+ * XXX This routine is the default, but what you are actually looking for
+ * is in main/asterisk.c, in ast_el_read_char(). XXX
+ */
+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 */
+ /* See main/asterisk.c: ast_el_read_char() */
+ 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/main/editline/read.h b/main/editline/read.h
new file mode 100644
index 000000000..b01e77db2
--- /dev/null
+++ b/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/main/editline/readline.c b/main/editline/readline.c
new file mode 100644
index 000000000..3a62df628
--- /dev/null
+++ b/main/editline/readline.c
@@ -0,0 +1,1664 @@
+/* $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;
+
+ 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/main/editline/readline/readline.h b/main/editline/readline/readline.h
new file mode 100644
index 000000000..7485dde40
--- /dev/null
+++ b/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/main/editline/refresh.c b/main/editline/refresh.c
new file mode 100644
index 000000000..935117741
--- /dev/null
+++ b/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 */
+#if 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/main/editline/refresh.h b/main/editline/refresh.h
new file mode 100644
index 000000000..33c0887c1
--- /dev/null
+++ b/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/main/editline/search.c b/main/editline/search.c
new file mode 100644
index 000000000..7c1cb84ef
--- /dev/null
+++ b/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/main/editline/search.h b/main/editline/search.h
new file mode 100644
index 000000000..676bbe2e3
--- /dev/null
+++ b/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/main/editline/sig.c b/main/editline/sig.c
new file mode 100644
index 000000000..0acba1247
--- /dev/null
+++ b/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/main/editline/sig.h b/main/editline/sig.h
new file mode 100644
index 000000000..e7231b65c
--- /dev/null
+++ b/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/main/editline/sys.h b/main/editline/sys.h
new file mode 100644
index 000000000..a306d1df7
--- /dev/null
+++ b/main/editline/sys.h
@@ -0,0 +1,133 @@
+/* $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_STRLCAT
+#define strlcat libedit_strlcat
+size_t strlcat(char *dst, const char *src, size_t size);
+#endif
+
+#ifndef HAVE_STRLCPY
+#define strlcpy libedit_strlcpy
+size_t strlcpy(char *dst, const char *src, size_t size);
+#endif
+
+#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>
+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/main/editline/term.c b/main/editline/term.c
new file mode 100644
index 000000000..fb627cabb
--- /dev/null
+++ b/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/main/editline/term.h b/main/editline/term.h
new file mode 100644
index 000000000..47e08e84b
--- /dev/null
+++ b/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/main/editline/tokenizer.c b/main/editline/tokenizer.c
new file mode 100644
index 000000000..f0de39bc9
--- /dev/null
+++ b/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/main/editline/tokenizer.h b/main/editline/tokenizer.h
new file mode 100644
index 000000000..7cc7a3346
--- /dev/null
+++ b/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/main/editline/tty.c b/main/editline/tty.c
new file mode 100644
index 000000000..256cf780b
--- /dev/null
+++ b/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/main/editline/tty.h b/main/editline/tty.h
new file mode 100644
index 000000000..e9597fceb
--- /dev/null
+++ b/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/main/editline/vi.c b/main/editline/vi.c
new file mode 100644
index 000000000..5683c7de0
--- /dev/null
+++ b/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/main/enum.c b/main/enum.c
new file mode 100644
index 000000000..3fa0bb977
--- /dev/null
+++ b/main/enum.c
@@ -0,0 +1,671 @@
+/*
+ * 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/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#if __APPLE_CC__ >= 1495
+#include <arpa/nameser_compat.h>
+#endif
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <regex.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/enum.h"
+#include "asterisk/dns.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/utils.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);
+
+struct naptr {
+ unsigned short order;
+ unsigned short pref;
+} __attribute__((__packed__));
+
+/*! \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;
+ }
+
+ if (option_debug > 2) /* Advanced NAPTR debugging */
+ ast_log(LOG_DEBUG, "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
+
+struct enum_naptr_rr {
+ struct naptr naptr; /* order and preference of RR */
+ char *result; /* result of naptr parsing,e.g.: tel:+5553 */
+ char *tech; /* Technology (from URL scheme) */
+ int sort_pos; /* sort position */
+};
+
+struct enum_context {
+ char *dst; /* Destination part of URL from ENUM */
+ int dstlen; /* Length */
+ char *tech; /* Technology (from URL scheme) */
+ int techlen; /* Length */
+ char *txt; /* TXT record in TXT lookup */
+ int txtlen; /* Length */
+ char *naptrinput; /* The number to lookup */
+ int position; /* used as counter for RRs or specifies position of required RR */
+ int options; /* options , see ENUMLOOKUP_OPTIONS_* defined above */
+ struct enum_naptr_rr *naptr_rrs; /* array of parsed NAPTR RRs */
+ int naptr_rrs_count; /* Size of array naptr_rrs */
+};
+
+/*! \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 = strdup(c->dst);
+ c->naptr_rrs[c->naptr_rrs_count].tech = 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;
+}
+
+/*! \brief 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 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;
+
+ 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_log(LOG_DEBUG, "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)
+ 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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "ast_get_enum: ast_search_dns(%s) returned %d\n", tmp, ret);
+ if (ret > 0)
+ break;
+ }
+ }
+
+ if (ret < 0) {
+ ast_log(LOG_DEBUG, "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);
+
+ for (k = 0; k < context.naptr_rrs_count; k++) {
+ free(context.naptr_rrs[k].result);
+ free(context.naptr_rrs[k].tech);
+ }
+
+ free(context.naptr_rrs);
+
+ return ret;
+}
+
+/*! \brief 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) {
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "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(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 */
+int ast_enum_init(void)
+{
+ struct ast_config *cfg;
+ struct enum_search *s, *sl;
+ struct ast_variable *v;
+
+ /* Destroy existing list */
+ ast_mutex_lock(&enumlock);
+ s = toplevs;
+ while(s) {
+ sl = s;
+ s = s->next;
+ free(sl);
+ }
+ toplevs = NULL;
+ cfg = ast_config_load("enum.conf");
+ 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);
+ return 0;
+}
+
+int ast_enum_reload(void)
+{
+ return ast_enum_init();
+}
diff --git a/main/file.c b/main/file.c
new file mode 100644
index 000000000..5836e21e3
--- /dev/null
+++ b/main/file.c
@@ -0,0 +1,1392 @@
+/*
+ * 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 <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "asterisk/frame.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/sched.h"
+#include "asterisk/options.h"
+#include "asterisk/translate.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/astobj2.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;
+
+static AST_LIST_HEAD_STATIC(formats, ast_format);
+
+int __ast_format_register(const struct ast_format *f, struct ast_module *mod)
+{
+ struct ast_format *tmp;
+
+ if (AST_LIST_LOCK(&formats)) {
+ ast_log(LOG_WARNING, "Unable to lock format list\n");
+ return -1;
+ }
+ AST_LIST_TRAVERSE(&formats, tmp, list) {
+ if (!strcasecmp(f->name, tmp->name)) {
+ AST_LIST_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_LIST_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_LIST_INSERT_HEAD(&formats, tmp, list);
+ AST_LIST_UNLOCK(&formats);
+ if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_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;
+
+ if (AST_LIST_LOCK(&formats)) {
+ ast_log(LOG_WARNING, "Unable to lock format list\n");
+ return -1;
+ }
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&formats, tmp, list) {
+ if (!strcasecmp(name, tmp->name)) {
+ AST_LIST_REMOVE_CURRENT(&formats, list);
+ free(tmp);
+ res = 0;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&formats);
+
+ if (!res) {
+ if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_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_MAX_AUDIO) {
+ /* 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_log(LOG_DEBUG, "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);
+ ast_frfree(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, 0600)) < 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] == '/') {
+ if (asprintf(&fn, "%s.%s", filename, ext) < 0) {
+ ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
+ fn = NULL;
+ }
+ } else {
+ if (asprintf(&fn, "%s/sounds/%s.%s",
+ ast_config_AST_DATA_DIR, filename, ext) < 0) {
+ ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
+ fn = NULL;
+ }
+ }
+ 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 void filestream_destructor(void *arg)
+{
+ char *cmd = NULL;
+ size_t size = 0;
+ struct ast_filestream *f = arg;
+
+ /* Stop a running stream if there is one */
+ if (f->owner) {
+ if (f->fmt->format < AST_FORMAT_MAX_AUDIO) {
+ f->owner->stream = NULL;
+ AST_SCHED_DEL(f->owner->sched, f->owner->streamid);
+#ifdef HAVE_DAHDI
+ ast_settimeout(f->owner, 0, NULL, NULL);
+#endif
+ } else {
+ f->owner->vstream = NULL;
+ AST_SCHED_DEL(f->owner->sched, f->owner->vstreamid);
+ }
+ }
+ /* 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)
+ free(f->filename);
+ if (f->realfilename)
+ free(f->realfilename);
+ if (f->fmt->close)
+ f->fmt->close(f);
+ if (f->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);
+}
+
+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 = ao2_alloc(l, filestream_destructor)) == 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;
+
+ if (AST_LIST_LOCK(&formats)) {
+ ast_log(LOG_WARNING, "Unable to lock format list\n");
+ return res;
+ }
+ /* Check for a specific format */
+ AST_LIST_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 */
+ 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_MAX_AUDIO && fmt)) {
+ free(fn);
+ continue; /* not a supported format */
+ }
+ if ( (bfile = fopen(fn, "r")) == NULL) {
+ free(fn);
+ continue; /* cannot open file */
+ }
+ s = get_filestream(f, bfile);
+ if (!s) {
+ fclose(bfile);
+ free(fn); /* cannot allocate descriptor */
+ continue;
+ }
+ if (open_wrapper(s)) {
+ free(fn);
+ ast_closestream(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_MAX_AUDIO) {
+ if (chan->stream)
+ ast_closestream(chan->stream);
+ chan->stream = s;
+ } else {
+ if (chan->vstream)
+ ast_closestream(chan->vstream);
+ chan->vstream = s;
+ }
+ 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));
+ free(nfn);
+ }
+ }
+ break;
+
+ default:
+ ast_log(LOG_WARNING, "Unknown helper %d\n", action);
+ }
+ free(fn);
+ }
+ }
+ AST_LIST_UNLOCK(&formats);
+ return res;
+}
+
+static int is_absolute_path(const char *filename)
+{
+ return filename[0] == '/';
+}
+
+static int fileexists_test(const char *filename, const char *fmt, const char *lang,
+ char *buf, int buflen)
+{
+ if (buf == NULL) {
+ return -1;
+ }
+
+ if (ast_language_is_prefix && !is_absolute_path(filename)) { /* new layout */
+ if (lang) {
+ snprintf(buf, buflen, "%s/%s", lang, filename);
+ } else {
+ snprintf(buf, buflen, "%s", filename);
+ }
+ } else { /* old layout */
+ strcpy(buf, filename); /* first copy the full string */
+ if (lang) {
+ /* insert the language and suffix if needed */
+ const char *c = strrchr(filename, '/');
+ int offset = c ? c - filename + 1 : 0; /* points right after the last '/' */
+ snprintf(buf + offset, buflen - offset, "%s/%s", lang, filename + offset);
+ }
+ }
+
+ return ast_filehelper(buf, NULL, fmt, ACTION_EXISTS);
+}
+
+/*!
+ * \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;
+ char *lang = NULL;
+
+ if (buf == NULL) {
+ return -1;
+ }
+
+ /* We try languages in the following order:
+ * preflang (may include dialect)
+ * lang (preflang without dialect - if any)
+ * <none>
+ * default (unless the same as preflang or lang without dialect)
+ */
+
+ /* Try preferred language */
+ if (!ast_strlen_zero(preflang)) {
+ /* try the preflang exactly as it was requested */
+ if ((res = fileexists_test(filename, fmt, preflang, buf, buflen)) > 0) {
+ return res;
+ } else {
+ /* try without a dialect */
+ char *postfix = NULL;
+ postfix = lang = ast_strdupa(preflang);
+
+ strsep(&postfix, "_");
+ if (postfix) {
+ if ((res = fileexists_test(filename, fmt, lang, buf, buflen)) > 0) {
+ return res;
+ }
+ }
+ }
+ }
+
+ /* Try without any language */
+ if ((res = fileexists_test(filename, fmt, NULL, buf, buflen)) > 0) {
+ return res;
+ }
+
+ /* Finally try the default language unless it was already tried before */
+ if ((ast_strlen_zero(preflang) || strcmp(preflang, DEFAULT_LANGUAGE)) && (ast_strlen_zero(lang) || strcmp(lang, DEFAULT_LANGUAGE))) {
+ if ((res = fileexists_test(filename, fmt, DEFAULT_LANGUAGE, buf, buflen)) > 0) {
+ return res;
+ }
+ }
+
+ return 0;
+}
+
+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) + 4;
+ 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) + 4;
+ buf = alloca(buflen);
+ if (buf == NULL)
+ return NULL;
+
+ for (format = AST_FORMAT_MAX_AUDIO << 1; format <= AST_FORMAT_MAX_VIDEO; 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);
+ if (f) {
+ ast_set_flag(f, AST_FRFLAG_FROM_FILESTREAM);
+ ao2_ref(s, +1);
+ }
+ 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_DAHDI
+ if (s->owner->timingfd > -1) {
+ int zap_timer_samples = whennext;
+ int rate;
+ /* whennext is in samples, but DAHDI timers operate in 8 kHz samples. */
+ if ((rate = ast_format_rate(s->fmt->format)) != 8000) {
+ float factor;
+ factor = ((float) rate) / ((float) 8000.0);
+ zap_timer_samples = (int) ( ((float) zap_timer_samples) / factor );
+ }
+ ast_settimeout(s->owner, zap_timer_samples, ast_fsread_audio, s);
+ } else
+#endif
+ s->owner->streamid = ast_sched_add(s->owner->sched,
+ whennext / (ast_format_rate(s->fmt->format) / 1000),
+ ast_fsread_audio, s);
+ s->lasttimeout = whennext;
+ return FSREAD_SUCCESS_NOSCHED;
+ }
+ return FSREAD_SUCCESS_SCHED;
+
+return_failure:
+ s->owner->streamid = -1;
+#ifdef HAVE_DAHDI
+ 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 / (ast_format_rate(s->fmt->format) / 1000),
+ 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_MAX_AUDIO)
+ 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)
+{
+
+ if (ast_test_flag(&f->fr, AST_FRFLAG_FROM_FILESTREAM)) {
+ /* If this flag is still set, it essentially means that the reference
+ * count of f is non-zero. We can't destroy this filestream until
+ * whatever is using the filestream's frame has finished.
+ *
+ * Since this was called, however, we need to remove the reference from
+ * when this filestream was first allocated. That way, when the embedded
+ * frame is freed, the refcount will reach 0 and we can finish destroying
+ * this filestream properly.
+ */
+ ao2_ref(f, -1);
+ return 0;
+ }
+
+ ao2_ref(f, -1);
+ 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) + 4; /* 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_log(LOG_DEBUG, "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);
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "<%s> Playing '%s' (language '%s')\n", chan->name, filename, 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;
+
+ if (AST_LIST_LOCK(&formats)) {
+ ast_log(LOG_WARNING, "Unable to lock format list\n");
+ return NULL;
+ }
+
+ AST_LIST_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_closestream(fs);
+ }
+ fs = NULL;
+ bfile = NULL;
+ free(fn);
+ continue;
+ }
+ /* found it */
+ fs->trans = NULL;
+ fs->fmt = f;
+ fs->flags = flags;
+ fs->mode = mode;
+ fs->filename = strdup(filename);
+ fs->vfs = NULL;
+ break;
+ }
+
+ AST_LIST_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;
+
+ if (AST_LIST_LOCK(&formats)) {
+ ast_log(LOG_WARNING, "Unable to lock format list\n");
+ return NULL;
+ }
+
+ /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
+ /* 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_LIST_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);
+ 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_closestream(fs);
+ fs = NULL;
+ }
+ continue;
+ }
+ fs->trans = NULL;
+ fs->fmt = f;
+ fs->flags = flags;
+ fs->mode = mode;
+ if (orig_fn) {
+ fs->realfilename = strdup(orig_fn);
+ fs->filename = strdup(fn);
+ } else {
+ fs->realfilename = NULL;
+ fs->filename = 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)
+ free(fn);
+ }
+
+ AST_LIST_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_SRCUPDATE:
+ 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) {
+ if (write(audiofd, fr->data, fr->datalen) < 0) {
+ ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
+ }
+ }
+ 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);
+}
+
+void ast_filestream_frame_freed(struct ast_frame *fr)
+{
+ struct ast_filestream *fs;
+
+ ast_clear_flag(fr, AST_FRFLAG_FROM_FILESTREAM);
+
+ fs = (struct ast_filestream *) (((char *) fr) - offsetof(struct ast_filestream, fr));
+
+ ao2_ref(fs, -1);
+}
+
+/*
+ * 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 *language, const char *digits)
+{
+ int res = 0;
+ if (!ast_strlen_zero(file)) {
+ res = ast_streamfile(chan, file, language);
+ if (!res)
+ res = ast_waitstream(chan, digits);
+ }
+ return res;
+}
+
+static int show_file_formats(int fd, int argc, char *argv[])
+{
+#define FORMAT "%-10s %-10s %-20s\n"
+#define FORMAT2 "%-10s %-10s %-20s\n"
+ struct ast_format *f;
+ int count_fmt = 0;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ ast_cli(fd, FORMAT, "Format", "Name", "Extensions");
+
+ if (AST_LIST_LOCK(&formats)) {
+ ast_log(LOG_WARNING, "Unable to lock format list\n");
+ return -1;
+ }
+
+ AST_LIST_TRAVERSE(&formats, f, list) {
+ ast_cli(fd, FORMAT2, ast_getformatname(f->format), f->name, f->exts);
+ count_fmt++;
+ }
+ AST_LIST_UNLOCK(&formats);
+ ast_cli(fd, "%d file formats registered.\n", count_fmt);
+ return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static int show_file_formats_deprecated(int fd, int argc, char *argv[])
+{
+#define FORMAT "%-10s %-10s %-20s\n"
+#define FORMAT2 "%-10s %-10s %-20s\n"
+ struct ast_format *f;
+ int count_fmt = 0;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ ast_cli(fd, FORMAT, "Format", "Name", "Extensions");
+
+ if (AST_LIST_LOCK(&formats)) {
+ ast_log(LOG_WARNING, "Unable to lock format list\n");
+ return -1;
+ }
+
+ AST_LIST_TRAVERSE(&formats, f, list) {
+ ast_cli(fd, FORMAT2, ast_getformatname(f->format), f->name, f->exts);
+ count_fmt++;
+ }
+ AST_LIST_UNLOCK(&formats);
+ ast_cli(fd, "%d file formats registered.\n", count_fmt);
+ return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+char show_file_formats_usage[] =
+"Usage: core show file formats\n"
+" Displays currently registered file formats (if any)\n";
+
+struct ast_cli_entry cli_show_file_formats_deprecated = {
+ { "show", "file", "formats" },
+ show_file_formats_deprecated, NULL,
+ NULL };
+
+struct ast_cli_entry cli_file[] = {
+ { { "core", "show", "file", "formats" },
+ show_file_formats, "Displays file formats",
+ show_file_formats_usage, NULL, &cli_show_file_formats_deprecated },
+};
+
+int ast_file_init(void)
+{
+ ast_cli_register_multiple(cli_file, sizeof(cli_file) / sizeof(struct ast_cli_entry));
+ return 0;
+}
diff --git a/main/fixedjitterbuf.c b/main/fixedjitterbuf.c
new file mode 100644
index 000000000..1d7a5cc30
--- /dev/null
+++ b/main/fixedjitterbuf.c
@@ -0,0 +1,351 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <unistd.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(struct fixed_jb_frame));
+}
+
+static inline void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame)
+{
+ 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);
+
+ 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/main/fixedjitterbuf.h b/main/fixedjitterbuf.h
new file mode 100644
index 000000000..868e24b66
--- /dev/null
+++ b/main/fixedjitterbuf.h
@@ -0,0 +1,92 @@
+/*
+ * 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/main/frame.c b/main/frame.c
new file mode 100644
index 000000000..b73782096
--- /dev/null
+++ b/main/frame.c
@@ -0,0 +1,1607 @@
+/*
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/frame.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.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"
+#include "asterisk/dsp.h"
+#include "asterisk/file.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, frame_cache_init, 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[] = { /*!< Bit number: comment - Bit numbers are hard coded in show_codec() */
+ { 1, AST_FORMAT_G723_1 , "g723" , "G.723.1", 24, 30, 300, 30, 30 }, /*!< 1 */
+ { 1, AST_FORMAT_GSM, "gsm" , "GSM", 33, 20, 300, 20, 20 }, /*!< 2: codec_gsm.c */
+ { 1, AST_FORMAT_ULAW, "ulaw", "G.711 u-law", 80, 10, 150, 10, 20 }, /*!< 3: codec_ulaw.c */
+ { 1, AST_FORMAT_ALAW, "alaw", "G.711 A-law", 80, 10, 150, 10, 20 }, /*!< 4: codec_alaw.c */
+ { 1, AST_FORMAT_G726, "g726", "G.726 RFC3551", 40, 10, 300, 10, 20 }, /*!< 5: codec_g726.c */
+ { 1, AST_FORMAT_ADPCM, "adpcm" , "ADPCM", 40, 10, 300, 10, 20 }, /*!< 6: codec_adpcm.c */
+ { 1, AST_FORMAT_SLINEAR, "slin", "16 bit Signed Linear PCM", 160, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE }, /*!< 7 */
+ { 1, AST_FORMAT_LPC10, "lpc10", "LPC10", 7, 20, 20, 20, 20 }, /*!< 8: codec_lpc10.c */
+ { 1, AST_FORMAT_G729A, "g729", "G.729A", 10, 10, 230, 10, 20, AST_SMOOTHER_FLAG_G729 }, /*!< 9: Binary commercial distribution */
+ { 1, AST_FORMAT_SPEEX, "speex", "SpeeX", 10, 10, 60, 10, 20 }, /*!< 10: codec_speex.c */
+ { 1, AST_FORMAT_ILBC, "ilbc", "iLBC", 50, 30, 30, 30, 30 }, /*!< 11: codec_ilbc.c */ /* inc=30ms - workaround */
+ { 1, AST_FORMAT_G726_AAL2, "g726aal2", "G.726 AAL2", 40, 10, 300, 10, 20 }, /*!< 12: codec_g726.c */
+ { 1, AST_FORMAT_G722, "g722", "G722"}, /*!< 13 */
+ { 0, 0, "nothing", "undefined" },
+ { 0, 0, "nothing", "undefined" },
+ { 0, 0, "nothing", "undefined" },
+ { 0, 0, "nothing", "undefined" },
+ { 0, AST_FORMAT_MAX_AUDIO, "maxaudio", "Maximum audio format" },
+ { 1, AST_FORMAT_JPEG, "jpeg", "JPEG image"}, /*!< 17: See format_jpeg.c */
+ { 1, AST_FORMAT_PNG, "png", "PNG image"}, /*!< 18: Image format */
+ { 1, AST_FORMAT_H261, "h261", "H.261 Video" }, /*!< 19: Video Passthrough */
+ { 1, AST_FORMAT_H263, "h263", "H.263 Video" }, /*!< 20: Passthrough support, see format_h263.c */
+ { 1, AST_FORMAT_H263_PLUS, "h263p", "H.263+ Video" }, /*!< 21: See format_h263.c */
+ { 1, AST_FORMAT_H264, "h264", "H.264 Video" }, /*!< 22: Passthrough support, see format_h263.c */
+ { 0, 0, "nothing", "undefined" },
+ { 0, 0, "nothing", "undefined" },
+ { 0, 0, "nothing", "undefined" },
+ { 0, AST_FORMAT_MAX_VIDEO, "maxvideo", "Maximum video format" },
+};
+
+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;
+ }
+ }
+ 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)
+{
+ 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)))
+ free(f);
+
+ 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);
+ } else if (ast_test_flag(fr, AST_FRFLAG_FROM_DSP)) {
+ ast_dsp_frame_freed(fr);
+ } else if (ast_test_flag(fr, AST_FRFLAG_FROM_FILESTREAM)) {
+ ast_filestream_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)
+ free(fr->data - fr->offset);
+ }
+ if (fr->mallocd & AST_MALLOCD_SRC) {
+ if (fr->src)
+ 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
+ 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);
+ ast_clear_flag(fr, AST_FRFLAG_FROM_DSP);
+
+ 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)
+ 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)
+ free((void *) out->src);
+ if (out != fr)
+ 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(&frames->list, 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) {
+ /* This may seem a little strange, but it's to avoid a gcc (4.2.4) compiler warning */
+ char *src;
+ out->src = buf + sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen;
+ src = (char *) out->src;
+ /* Must have space since we allocated for it */
+ strcpy(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].visible && 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].visible && (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)
+ snprintf(start, size, "nothing)");
+ else if (size > 1)
+ *(end -1) = ')';
+ return buf;
+}
+
+static struct ast_codec_alias_table {
+ char *alias;
+ char *realname;
+} ast_codec_alias_table[] = {
+ { "slinear", "slin"},
+ { "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(AST_FORMAT_LIST[x].visible && (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].visible && AST_FORMAT_LIST[x].bits == codec) {
+ ret = AST_FORMAT_LIST[x].desc;
+ break;
+ }
+ }
+ return ret;
+}
+
+static int show_codecs_deprecated(int fd, int argc, char *argv[])
+{
+ int i, found=0;
+ char hex[25];
+
+ if ((argc < 2) || (argc > 3))
+ return RESULT_SHOWUSAGE;
+
+ if (!ast_opt_dont_warn)
+ ast_cli(fd, "Disclaimer: this command is for informational purposes only.\n"
+ "\tIt does not indicate anything about your configuration.\n");
+
+ ast_cli(fd, "%11s %9s %10s TYPE %8s %s\n","INT","BINARY","HEX","NAME","DESC");
+ ast_cli(fd, "--------------------------------------------------------------------------------\n");
+ if ((argc == 2) || (!strcasecmp(argv[1],"audio"))) {
+ found = 1;
+ for (i=0;i<13;i++) {
+ snprintf(hex,25,"(0x%x)",1<<i);
+ ast_cli(fd, "%11u (1 << %2d) %10s audio %8s (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
+ }
+ }
+
+ if ((argc == 2) || (!strcasecmp(argv[1],"image"))) {
+ found = 1;
+ for (i=16;i<18;i++) {
+ snprintf(hex,25,"(0x%x)",1<<i);
+ ast_cli(fd, "%11u (1 << %2d) %10s image %8s (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
+ }
+ }
+
+ if ((argc == 2) || (!strcasecmp(argv[1],"video"))) {
+ found = 1;
+ for (i=18;i<22;i++) {
+ snprintf(hex,25,"(0x%x)",1<<i);
+ ast_cli(fd, "%11u (1 << %2d) %10s video %8s (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
+ }
+ }
+
+ if (! found)
+ return RESULT_SHOWUSAGE;
+ else
+ return RESULT_SUCCESS;
+}
+
+static int show_codecs(int fd, int argc, char *argv[])
+{
+ int i, found=0;
+ char hex[25];
+
+ if ((argc < 3) || (argc > 4))
+ return RESULT_SHOWUSAGE;
+
+ if (!ast_opt_dont_warn)
+ ast_cli(fd, "Disclaimer: this command is for informational purposes only.\n"
+ "\tIt does not indicate anything about your configuration.\n");
+
+ ast_cli(fd, "%11s %9s %10s TYPE %8s %s\n","INT","BINARY","HEX","NAME","DESC");
+ ast_cli(fd, "--------------------------------------------------------------------------------\n");
+ if ((argc == 3) || (!strcasecmp(argv[3],"audio"))) {
+ found = 1;
+ for (i=0;i<13;i++) {
+ snprintf(hex,25,"(0x%x)",1<<i);
+ ast_cli(fd, "%11u (1 << %2d) %10s audio %8s (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
+ }
+ }
+
+ if ((argc == 3) || (!strcasecmp(argv[3],"image"))) {
+ found = 1;
+ for (i=16;i<18;i++) {
+ snprintf(hex,25,"(0x%x)",1<<i);
+ ast_cli(fd, "%11u (1 << %2d) %10s image %8s (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
+ }
+ }
+
+ if ((argc == 3) || (!strcasecmp(argv[3],"video"))) {
+ found = 1;
+ for (i=18;i<22;i++) {
+ snprintf(hex,25,"(0x%x)",1<<i);
+ ast_cli(fd, "%11u (1 << %2d) %10s video %8s (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
+ }
+ }
+
+ if (! found)
+ return RESULT_SHOWUSAGE;
+ else
+ return RESULT_SUCCESS;
+}
+
+static char frame_show_codecs_usage[] =
+"Usage: core show codecs [audio|video|image]\n"
+" Displays codec mapping\n";
+
+static int show_codec_n_deprecated(int fd, int argc, char *argv[])
+{
+ int codec, i, found=0;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ if (sscanf(argv[2],"%d",&codec) != 1)
+ return RESULT_SHOWUSAGE;
+
+ for (i = 0; i < 32; i++)
+ if (codec & (1 << i)) {
+ found = 1;
+ ast_cli(fd, "%11u (1 << %2d) %s\n",1 << i,i,ast_codec2str(1<<i));
+ }
+
+ if (!found)
+ ast_cli(fd, "Codec %d not found\n", codec);
+
+ return RESULT_SUCCESS;
+}
+
+static int show_codec_n(int fd, int argc, char *argv[])
+{
+ int codec, i, found=0;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ if (sscanf(argv[3],"%d",&codec) != 1)
+ return RESULT_SHOWUSAGE;
+
+ for (i = 0; i < 32; i++)
+ if (codec & (1 << i)) {
+ found = 1;
+ ast_cli(fd, "%11u (1 << %2d) %s\n",1 << i,i,ast_codec2str(1<<i));
+ }
+
+ if (!found)
+ ast_cli(fd, "Codec %d not found\n", codec);
+
+ return RESULT_SUCCESS;
+}
+
+static char frame_show_codec_n_usage[] =
+"Usage: core show codec <number>\n"
+" Displays codec mapping\n";
+
+/*! 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 int show_frame_stats(int fd, int argc, char *argv[])
+{
+ struct ast_frame *f;
+ int x=1;
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ AST_LIST_LOCK(&headerlist);
+ ast_cli(fd, " Framer Statistics \n");
+ ast_cli(fd, "---------------------------\n");
+ ast_cli(fd, "Total allocated headers: %d\n", headers);
+ ast_cli(fd, "Queue Dump:\n");
+ AST_LIST_TRAVERSE(&headerlist, f, frame_list)
+ ast_cli(fd, "%d. Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
+ AST_LIST_UNLOCK(&headerlist);
+ return RESULT_SUCCESS;
+}
+
+static char frame_stats_usage[] =
+"Usage: core show frame stats\n"
+" Displays debugging statistics from framer\n";
+#endif
+
+/* Builtin Asterisk CLI-commands for debugging */
+static struct ast_cli_entry cli_show_codecs = {
+ { "show", "codecs", NULL },
+ show_codecs_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_audio_codecs = {
+ { "show", "audio", "codecs", NULL },
+ show_codecs_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_video_codecs = {
+ { "show", "video", "codecs", NULL },
+ show_codecs_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_image_codecs = {
+ { "show", "image", "codecs", NULL },
+ show_codecs_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_codec = {
+ { "show", "codec", NULL },
+ show_codec_n_deprecated, NULL,
+ NULL };
+
+#ifdef TRACE_FRAMES
+static struct ast_cli_entry cli_show_frame_stats = {
+ { "show", "frame", "stats", NULL },
+ show_frame_stats, NULL,
+ NULL };
+#endif
+
+static struct ast_cli_entry my_clis[] = {
+ { { "core", "show", "codecs", NULL },
+ show_codecs, "Displays a list of codecs",
+ frame_show_codecs_usage, NULL, &cli_show_codecs },
+
+ { { "core", "show", "audio", "codecs", NULL },
+ show_codecs, "Displays a list of audio codecs",
+ frame_show_codecs_usage, NULL, &cli_show_audio_codecs },
+
+ { { "core", "show", "video", "codecs", NULL },
+ show_codecs, "Displays a list of video codecs",
+ frame_show_codecs_usage, NULL, &cli_show_video_codecs },
+
+ { { "core", "show", "image", "codecs", NULL },
+ show_codecs, "Displays a list of image codecs",
+ frame_show_codecs_usage, NULL, &cli_show_image_codecs },
+
+ { { "core", "show", "codec", NULL },
+ show_codec_n, "Shows a specific codec",
+ frame_show_codec_n_usage, NULL, &cli_show_codec },
+
+#ifdef TRACE_FRAMES
+ { { "core", "show", "frame", "stats", NULL },
+ show_frame_stats, "Shows frame statistics",
+ frame_stats_usage, NULL, &cli_show_frame_stats },
+#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 - 1); /* safe */
+ total_len -= slen;
+ }
+ if(total_len && x < 31 && ast_codec_pref_index(pref , x + 1)) {
+ strncat(buf, "|", total_len - 1); /* safe */
+ total_len--;
+ }
+ }
+ if(total_len) {
+ strncat(buf, ")", total_len - 1); /* safe */
+ 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;
+
+ if (option_debug > 3)
+ ast_log(LOG_DEBUG, "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;
+}
+
+void ast_parse_allow_disallow(struct ast_codec_pref *pref, int *mask, const char *list, int allowing)
+{
+ 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';
+ if (option_debug)
+ ast_log(LOG_DEBUG,"Packetization for codec: %s is %s\n", this, psize);
+ framems = atoi(psize);
+ if (framems < 0)
+ framems = 0;
+ }
+ if (!(format = ast_getformatbyname(this))) {
+ ast_log(LOG_WARNING, "Cannot %s unknown format '%s'\n", allowing ? "allow" : "disallow", this);
+ 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));
+ }
+ }
+ }
+}
+
+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:
+ 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_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:
+ 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/main/fskmodem.c b/main/fskmodem.c
new file mode 100644
index 000000000..3ab462886
--- /dev/null
+++ b/main/fskmodem.c
@@ -0,0 +1,309 @@
+/*
+ * 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 <stdio.h>
+
+#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 float get_sample(short **buffer, int *len)
+{
+ float retval;
+ retval = (float) **buffer / 256;
+ (*buffer)++;
+ (*len)--;
+ return retval;
+}
+
+#define GET_SAMPLE get_sample(&buffer, len)
+
+/* Coeficientes para filtros de entrada */
+/* Tabla de coeficientes, generada a partir del programa "mkfilter" */
+/* Formato: coef[IDX_FREC][IDX_BW][IDX_COEF] */
+/* IDX_COEF=0 => 1/GAIN */
+/* IDX_COEF=1-6 => Coeficientes y[n] */
+
+static double coef_in[NF][NBW][8]={
+#include "coef_in.h"
+};
+
+/* Coeficientes para filtro de salida */
+/* Tabla de coeficientes, generada a partir del programa "mkfilter" */
+/* Formato: coef[IDX_BW][IDX_COEF] */
+/* IDX_COEF=0 => 1/GAIN */
+/* IDX_COEF=1-6 => Coeficientes y[n] */
+
+static double coef_out[NBW][8]={
+#include "coef_out.h"
+};
+
+
+/*! Filtro pasa-banda para frecuencia de MARCA */
+static inline float filtroM(fsk_data *fskd,float in)
+{
+ int i,j;
+ double s;
+ double *pc;
+
+ pc=&coef_in[fskd->f_mark_idx][fskd->bw][0];
+ fskd->fmxv[(fskd->fmp+6)&7]=in*(*pc++);
+
+ s=(fskd->fmxv[(fskd->fmp+6)&7] - fskd->fmxv[fskd->fmp]) + 3 * (fskd->fmxv[(fskd->fmp+2)&7] - fskd->fmxv[(fskd->fmp+4)&7]);
+ for (i=0,j=fskd->fmp;i<6;i++,j++) s+=fskd->fmyv[j&7]*(*pc++);
+ fskd->fmyv[j&7]=s;
+ fskd->fmp++; fskd->fmp&=7;
+ return s;
+}
+
+/*! Filtro pasa-banda para frecuencia de ESPACIO */
+static inline float filtroS(fsk_data *fskd,float in)
+{
+ int i,j;
+ double s;
+ double *pc;
+
+ pc=&coef_in[fskd->f_space_idx][fskd->bw][0];
+ fskd->fsxv[(fskd->fsp+6)&7]=in*(*pc++);
+
+ s=(fskd->fsxv[(fskd->fsp+6)&7] - fskd->fsxv[fskd->fsp]) + 3 * (fskd->fsxv[(fskd->fsp+2)&7] - fskd->fsxv[(fskd->fsp+4)&7]);
+ for (i=0,j=fskd->fsp;i<6;i++,j++) s+=fskd->fsyv[j&7]*(*pc++);
+ fskd->fsyv[j&7]=s;
+ fskd->fsp++; fskd->fsp&=7;
+ return s;
+}
+
+/*! Filtro pasa-bajos para datos demodulados */
+static inline float filtroL(fsk_data *fskd,float in)
+{
+ int i,j;
+ double s;
+ double *pc;
+
+ pc=&coef_out[fskd->bw][0];
+ fskd->flxv[(fskd->flp + 6) & 7]=in * (*pc++);
+
+ s= (fskd->flxv[fskd->flp] + fskd->flxv[(fskd->flp+6)&7]) +
+ 6 * (fskd->flxv[(fskd->flp+1)&7] + fskd->flxv[(fskd->flp+5)&7]) +
+ 15 * (fskd->flxv[(fskd->flp+2)&7] + fskd->flxv[(fskd->flp+4)&7]) +
+ 20 * fskd->flxv[(fskd->flp+3)&7];
+
+ for (i=0,j=fskd->flp;i<6;i++,j++) s+=fskd->flyv[j&7]*(*pc++);
+ fskd->flyv[j&7]=s;
+ fskd->flp++; fskd->flp&=7;
+ return s;
+}
+
+static inline int demodulador(fsk_data *fskd, float *retval, float x)
+{
+ float xS,xM;
+
+ fskd->cola_in[fskd->pcola]=x;
+
+ xS=filtroS(fskd,x);
+ xM=filtroM(fskd,x);
+
+ fskd->cola_filtro[fskd->pcola]=xM-xS;
+
+ x=filtroL(fskd,xM*xM - xS*xS);
+
+ fskd->cola_demod[fskd->pcola++]=x;
+ fskd->pcola &= (NCOLA-1);
+
+ *retval = x;
+ return(0);
+}
+
+static int get_bit_raw(fsk_data *fskd, short *buffer, int *len)
+{
+ /* Esta funcion implementa un DPLL para sincronizarse con los bits */
+ float x,spb,spb2,ds;
+ int f;
+
+ spb=fskd->spb;
+ if (fskd->spb == 7) spb = 8000.0 / 1200.0;
+ ds=spb/32.;
+ spb2=spb/2.;
+
+ for (f=0;;){
+ if (demodulador(fskd,&x, GET_SAMPLE)) return(-1);
+ if ((x*fskd->x0)<0) { /* Transicion */
+ if (!f) {
+ if (fskd->cont<(spb2)) fskd->cont+=ds; else fskd->cont-=ds;
+ f=1;
+ }
+ }
+ fskd->x0=x;
+ fskd->cont+=1.;
+ if (fskd->cont>spb) {
+ fskd->cont-=spb;
+ break;
+ }
+ }
+ f=(x>0)?0x80:0;
+ return(f);
+}
+
+int fsk_serie(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;
+ }
+ /* Esperamos bit de start */
+ 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 */
+/*
+ if (demodulador(zap,&x1)) return(-1);
+ for(;;) {
+ if (demodulador(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 (demodulador(fskd,&fskd->x1,GET_SAMPLE)) return(-1);
+ samples++;
+ for(;;)
+ {
+search_startbit2:
+ if (*len <= 0) {
+ fskd->state = STATE_SEARCH_STARTBIT2;
+ return 0;
+ }
+ samples++;
+ if (demodulador(fskd,&fskd->x2,GET_SAMPLE)) return(-1);
+#if 0
+ printf("x2 = %5.5f ", fskd->x2);
+#endif
+ if (fskd->x2 < -0.5) break;
+ }
+search_startbit3:
+ /* Esperamos 0.5 bits antes de usar DPLL */
+ i=fskd->spb/2;
+ if (*len < i) {
+ fskd->state = STATE_SEARCH_STARTBIT3;
+ return 0;
+ }
+ for(;i>0;i--) { if (demodulador(fskd,&fskd->x1,GET_SAMPLE)) return(-1);
+#if 0
+ printf("x1 = %5.5f ", fskd->x1);
+#endif
+ samples++; }
+
+ /* x1 debe ser negativo (confirmación del bit de start) */
+
+ } while (fskd->x1>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;
+ }
+ /* Leemos ahora los bits de datos */
+ 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;
+
+ /* Leemos bit de paridad (si existe) y la comprobamos */
+ if (fskd->paridad) {
+ olen = *len;
+ i=get_bit_raw(fskd, buffer, len);
+ buffer += (olen - *len);
+ if (i == -1) return(-1);
+ if (i) n1++;
+ if (fskd->paridad==1) { /* paridad=1 (par) */
+ if (n1&1) a|=0x100; /* error */
+ } else { /* paridad=2 (impar) */
+ if (!(n1&1)) a|=0x100; /* error */
+ }
+ }
+
+ /* Leemos bits de STOP. Todos deben ser 1 */
+
+ for (j=fskd->nstop;j;j--) {
+ r = get_bit_raw(fskd, buffer, len);
+ if (r == -1) return(-1);
+ if (!r) a|=0x200;
+ }
+
+ /* Por fin retornamos */
+ /* Bit 8 : Error de paridad */
+ /* Bit 9 : Error de Framming */
+
+ *outbyte = a;
+ fskd->state = STATE_SEARCH_STARTBIT;
+ return 1;
+}
diff --git a/main/global_datastores.c b/main/global_datastores.c
new file mode 100644
index 000000000..ca04a0fe9
--- /dev/null
+++ b/main/global_datastores.c
@@ -0,0 +1,113 @@
+/*
+ * 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;
+}
+
+
+static void *dial_features_duplicate(void *data)
+{
+ struct ast_dial_features *df = data, *df_copy;
+
+ if (!(df_copy = ast_calloc(1, sizeof(*df)))) {
+ return NULL;
+ }
+
+ memcpy(df_copy, df, sizeof(*df));
+
+ return df_copy;
+}
+
+static void dial_features_destroy(void *data) {
+ struct ast_dial_features *df = data;
+ if (df) {
+ ast_free(df);
+ }
+}
+
+const struct ast_datastore_info dialed_interface_info = {
+ .type = "dialed-interface",
+ .destroy = dialed_interface_destroy,
+ .duplicate = dialed_interface_duplicate,
+};
+
+const struct ast_datastore_info dial_features_info = {
+ .type = "dial-features",
+ .destroy = dial_features_destroy,
+ .duplicate = dial_features_duplicate,
+};
diff --git a/main/http.c b/main/http.c
new file mode 100644
index 000000000..cee1d913c
--- /dev/null
+++ b/main/http.c
@@ -0,0 +1,752 @@
+/*
+ * 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 <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include "asterisk/cli.h"
+#include "asterisk/http.h"
+#include "asterisk/utils.h"
+#include "asterisk/strings.h"
+#include "asterisk/options.h"
+#include "asterisk/config.h"
+#include "asterisk/version.h"
+#include "asterisk/manager.h"
+
+#define MAX_PREFIX 80
+#define DEFAULT_PREFIX "/asterisk"
+
+struct ast_http_server_instance {
+ FILE *f;
+ int fd;
+ struct sockaddr_in requestor;
+ ast_http_callback callback;
+};
+
+AST_RWLOCK_DEFINE_STATIC(uris_lock);
+static struct ast_http_uri *uris;
+
+static int httpfd = -1;
+static pthread_t master = AST_PTHREADT_NULL;
+static char prefix[MAX_PREFIX];
+static int prefix_len;
+static struct sockaddr_in oldsin;
+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" },
+};
+
+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 char *static_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
+{
+ char result[4096];
+ char *c=result;
+ char *path;
+ char *ftype;
+ const char *mtype;
+ char wkspace[80];
+ struct stat st;
+ int len;
+ int fd;
+ void *blob;
+
+ /* 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;
+
+ len = st.st_size + strlen(mtype) + 40;
+
+ blob = malloc(len);
+ if (blob) {
+ c = blob;
+ sprintf(c, "Content-type: %s\r\n\r\n", mtype);
+ c += strlen(c);
+ *contentlength = read(fd, c, st.st_size);
+ if (*contentlength < 0) {
+ close(fd);
+ free(blob);
+ goto out403;
+ }
+ }
+ close(fd);
+ return blob;
+
+out404:
+ *status = 404;
+ *title = strdup("Not Found");
+ return ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server.");
+
+out403:
+ *status = 403;
+ *title = strdup("Access Denied");
+ return ast_http_error(403, "Access Denied", NULL, "You do not have permission to access the requested URL.");
+}
+
+
+static char *httpstatus_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
+{
+ char result[4096];
+ size_t reslen = sizeof(result);
+ char *c=result;
+ struct ast_variable *v;
+
+ ast_build_string(&c, &reslen,
+ "\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_build_string(&c, &reslen, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
+ ast_build_string(&c, &reslen, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
+ ast_inet_ntoa(oldsin.sin_addr));
+ ast_build_string(&c, &reslen, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
+ ntohs(oldsin.sin_port));
+ ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+ v = vars;
+ while(v) {
+ if (strncasecmp(v->name, "cookie_", 7))
+ ast_build_string(&c, &reslen, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
+ v = v->next;
+ }
+ ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+ v = vars;
+ while(v) {
+ if (!strncasecmp(v->name, "cookie_", 7))
+ ast_build_string(&c, &reslen, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
+ v = v->next;
+ }
+ ast_build_string(&c, &reslen, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
+ return strdup(result);
+}
+
+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,
+};
+
+char *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
+{
+ char *c = NULL;
+ if (asprintf(&c,
+ "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) < 0) {
+ ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
+ c = NULL;
+ }
+ return c;
+}
+
+int ast_http_uri_link(struct ast_http_uri *urih)
+{
+ struct ast_http_uri *prev;
+
+ ast_rwlock_wrlock(&uris_lock);
+ prev = uris;
+ if (!uris || strlen(uris->uri) <= strlen(urih->uri)) {
+ urih->next = uris;
+ uris = urih;
+ } else {
+ while (prev->next && (strlen(prev->next->uri) > strlen(urih->uri)))
+ prev = prev->next;
+ /* Insert it here */
+ urih->next = prev->next;
+ prev->next = urih;
+ }
+ ast_rwlock_unlock(&uris_lock);
+
+ return 0;
+}
+
+void ast_http_uri_unlink(struct ast_http_uri *urih)
+{
+ struct ast_http_uri *prev;
+
+ ast_rwlock_wrlock(&uris_lock);
+ if (!uris) {
+ ast_rwlock_unlock(&uris_lock);
+ return;
+ }
+ prev = uris;
+ if (uris == urih) {
+ uris = uris->next;
+ }
+ while(prev->next) {
+ if (prev->next == urih) {
+ prev->next = urih->next;
+ break;
+ }
+ prev = prev->next;
+ }
+ ast_rwlock_unlock(&uris_lock);
+}
+
+static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status,
+ char **title, int *contentlength, struct ast_variable **cookies,
+ unsigned int *static_content)
+{
+ char *c;
+ char *turi;
+ char *params;
+ char *var;
+ char *val;
+ struct ast_http_uri *urih=NULL;
+ int len;
+ struct ast_variable *vars=NULL, *v, *prev = NULL;
+
+
+ params = strchr(uri, '?');
+ if (params) {
+ *params = '\0';
+ params++;
+ while ((var = strsep(&params, "&"))) {
+ val = strchr(var, '=');
+ if (val) {
+ *val = '\0';
+ 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;
+ }
+ }
+ }
+ if (prev)
+ prev->next = *cookies;
+ else
+ vars = *cookies;
+ *cookies = NULL;
+ ast_uri_decode(uri);
+ if (!strncasecmp(uri, prefix, prefix_len)) {
+ uri += prefix_len;
+ if (!*uri || (*uri == '/')) {
+ if (*uri == '/')
+ uri++;
+ ast_rwlock_rdlock(&uris_lock);
+ urih = uris;
+ while(urih) {
+ len = strlen(urih->uri);
+ if (!strncasecmp(urih->uri, uri, len)) {
+ if (!uri[len] || uri[len] == '/') {
+ turi = uri + len;
+ if (*turi == '/')
+ turi++;
+ if (!*turi || urih->has_subtree) {
+ uri = turi;
+ break;
+ }
+ }
+ }
+ urih = urih->next;
+ }
+ if (!urih)
+ ast_rwlock_unlock(&uris_lock);
+ }
+ }
+ if (urih) {
+ if (urih->static_content)
+ *static_content = 1;
+ c = urih->callback(sin, uri, vars, status, title, contentlength);
+ ast_rwlock_unlock(&uris_lock);
+ } else if (ast_strlen_zero(uri) && ast_strlen_zero(prefix)) {
+ /* Special case: If no prefix, and no URI, send to /static/index.html */
+ c = ast_http_error(302, "Moved Temporarily", "Location: /static/index.html\r\n", "Redirecting to /static/index.html.");
+ *status = 302;
+ *title = strdup("Moved Temporarily");
+ } else {
+ c = ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server.");
+ *status = 404;
+ *title = strdup("Not Found");
+ }
+ ast_variables_destroy(vars);
+ return c;
+}
+
+static struct ast_variable *parse_cookies(char *cookies)
+{
+ char *cur;
+ struct ast_variable *vars = NULL, *var;
+
+ /* Skip Cookie: */
+ cookies += 8;
+
+ while ((cur = strsep(&cookies, ";"))) {
+ char *name, *val;
+
+ name = val = cur;
+ strsep(&val, "=");
+
+ if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
+ continue;
+ }
+
+ name = ast_strip(name);
+ val = ast_strip_quoted(val, "\"", "\"");
+
+ if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
+ continue;
+ }
+
+ if (option_debug) {
+ ast_log(LOG_DEBUG, "mmm ... cookie! Name: '%s' Value: '%s'\n", name, val);
+ }
+
+ var = ast_variable_new(name, val);
+ var->next = vars;
+ vars = var;
+ }
+
+ return vars;
+}
+
+static void *ast_httpd_helper_thread(void *data)
+{
+ char buf[4096];
+ char cookie[4096];
+ char timebuf[256];
+ struct ast_http_server_instance *ser = data;
+ struct ast_variable *vars = NULL;
+ char *uri, *c, *title=NULL;
+ int status = 200, contentlength = 0;
+ time_t t;
+ unsigned int static_content = 0;
+
+ if (fgets(buf, sizeof(buf), ser->f)) {
+ /* Skip method */
+ uri = buf;
+ while(*uri && (*uri > 32))
+ uri++;
+ if (*uri) {
+ *uri = '\0';
+ uri++;
+ }
+
+ /* Skip white space */
+ while (*uri && (*uri < 33))
+ uri++;
+
+ if (*uri) {
+ c = uri;
+ while (*c && (*c > 32))
+ c++;
+ if (*c) {
+ *c = '\0';
+ }
+ }
+
+ while (fgets(cookie, sizeof(cookie), ser->f)) {
+ /* Trim trailing characters */
+ while(!ast_strlen_zero(cookie) && (cookie[strlen(cookie) - 1] < 33)) {
+ cookie[strlen(cookie) - 1] = '\0';
+ }
+ if (ast_strlen_zero(cookie))
+ break;
+ if (!strncasecmp(cookie, "Cookie: ", 8)) {
+ vars = parse_cookies(cookie);
+ }
+ }
+
+ if (*uri) {
+ if (!strcasecmp(buf, "get"))
+ c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars, &static_content);
+ else
+ c = ast_http_error(501, "Not Implemented", NULL, "Attempt to use unimplemented / unsupported method");\
+ } else
+ c = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
+
+ /* If they aren't mopped up already, clean up the cookies */
+ if (vars)
+ ast_variables_destroy(vars);
+
+ if (!c)
+ c = ast_http_error(500, "Internal Error", NULL, "Internal Server Error");
+ if (c) {
+ time(&t);
+ strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
+ ast_cli(ser->fd, "HTTP/1.1 %d %s\r\n", status, title ? title : "OK");
+ ast_cli(ser->fd, "Server: Asterisk/%s\r\n", ASTERISK_VERSION);
+ ast_cli(ser->fd, "Date: %s\r\n", timebuf);
+ ast_cli(ser->fd, "Connection: close\r\n");
+ if (!static_content)
+ ast_cli(ser->fd, "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) {
+ char *tmp;
+ tmp = strstr(c, "\r\n\r\n");
+ if (tmp) {
+ ast_cli(ser->fd, "Content-length: %d\r\n", contentlength);
+ if (write(ser->fd, c, (tmp + 4 - c)) < 0) {
+ ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
+ }
+ if (write(ser->fd, tmp + 4, contentlength) < 0) {
+ ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
+ }
+ }
+ } else
+ ast_cli(ser->fd, "%s", c);
+ free(c);
+ }
+ if (title)
+ free(title);
+ }
+ fclose(ser->f);
+ free(ser);
+ return NULL;
+}
+
+static void *http_root(void *data)
+{
+ int fd;
+ struct sockaddr_in sin;
+ socklen_t sinlen;
+ struct ast_http_server_instance *ser;
+ pthread_t launched;
+ pthread_attr_t attr;
+
+ for (;;) {
+ int flags;
+
+ ast_wait_for_input(httpfd, -1);
+ sinlen = sizeof(sin);
+ fd = accept(httpfd, (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;
+ memcpy(&ser->requestor, &sin, sizeof(ser->requestor));
+ if ((ser->f = fdopen(ser->fd, "w+"))) {
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ if (ast_pthread_create_background(&launched, &attr, ast_httpd_helper_thread, ser)) {
+ ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
+ fclose(ser->f);
+ free(ser);
+ }
+ pthread_attr_destroy(&attr);
+ } else {
+ ast_log(LOG_WARNING, "fdopen failed!\n");
+ close(ser->fd);
+ free(ser);
+ }
+ }
+ return NULL;
+}
+
+char *ast_http_setcookie(const char *var, const char *val, int expires, char *buf, size_t buflen)
+{
+ char *c;
+ c = buf;
+ ast_build_string(&c, &buflen, "Set-Cookie: %s=\"%s\"; Version=\"1\"", var, val);
+ if (expires)
+ ast_build_string(&c, &buflen, "; Max-Age=%d", expires);
+ ast_build_string(&c, &buflen, "\r\n");
+ return buf;
+}
+
+
+static void http_server_start(struct sockaddr_in *sin)
+{
+ int flags;
+ int x = 1;
+
+ /* Do nothing if nothing has changed */
+ if (!memcmp(&oldsin, sin, sizeof(oldsin))) {
+ ast_log(LOG_DEBUG, "Nothing changed in http\n");
+ return;
+ }
+
+ memcpy(&oldsin, sin, sizeof(oldsin));
+
+ /* Shutdown a running server if there is one */
+ if (master != AST_PTHREADT_NULL) {
+ pthread_cancel(master);
+ pthread_kill(master, SIGURG);
+ pthread_join(master, NULL);
+ }
+
+ if (httpfd != -1)
+ close(httpfd);
+
+ /* If there's no new server, stop here */
+ if (!sin->sin_family)
+ return;
+
+
+ httpfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (httpfd < 0) {
+ ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
+ return;
+ }
+
+ setsockopt(httpfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
+ if (bind(httpfd, (struct sockaddr *)sin, sizeof(*sin))) {
+ ast_log(LOG_NOTICE, "Unable to bind http server to %s:%d: %s\n",
+ ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
+ strerror(errno));
+ close(httpfd);
+ httpfd = -1;
+ return;
+ }
+ if (listen(httpfd, 10)) {
+ ast_log(LOG_NOTICE, "Unable to listen!\n");
+ close(httpfd);
+ httpfd = -1;
+ return;
+ }
+ flags = fcntl(httpfd, F_GETFL);
+ fcntl(httpfd, F_SETFL, flags | O_NONBLOCK);
+ if (ast_pthread_create_background(&master, NULL, http_root, NULL)) {
+ ast_log(LOG_NOTICE, "Unable to launch http server on %s:%d: %s\n",
+ ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
+ strerror(errno));
+ close(httpfd);
+ httpfd = -1;
+ }
+}
+
+static int __ast_http_load(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ int enabled=0;
+ int newenablestatic=0;
+ struct sockaddr_in sin;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ char newprefix[MAX_PREFIX];
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_port = htons(8088);
+
+ strcpy(newprefix, DEFAULT_PREFIX);
+
+ cfg = ast_config_load("http.conf");
+ if (cfg) {
+ v = ast_variable_browse(cfg, "general");
+ while(v) {
+ if (!strcasecmp(v->name, "enabled"))
+ enabled = ast_true(v->value);
+ else if (!strcasecmp(v->name, "enablestatic"))
+ newenablestatic = ast_true(v->value);
+ else if (!strcasecmp(v->name, "bindport"))
+ sin.sin_port = ntohs(atoi(v->value));
+ else if (!strcasecmp(v->name, "bindaddr")) {
+ if ((hp = ast_gethostbyname(v->value, &ahp))) {
+ memcpy(&sin.sin_addr, hp->h_addr, sizeof(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';
+ }
+
+ }
+ v = v->next;
+ }
+ ast_config_destroy(cfg);
+ }
+ if (enabled)
+ sin.sin_family = AF_INET;
+ if (strcmp(prefix, newprefix)) {
+ ast_copy_string(prefix, newprefix, sizeof(prefix));
+ prefix_len = strlen(prefix);
+ }
+ enablestatic = newenablestatic;
+
+ http_server_start(&sin);
+
+
+ return 0;
+}
+
+static int handle_show_http(int fd, int argc, char *argv[])
+{
+ struct ast_http_uri *urih;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ ast_cli(fd, "HTTP Server Status:\n");
+ ast_cli(fd, "Prefix: %s\n", prefix);
+ if (oldsin.sin_family)
+ ast_cli(fd, "Server Enabled and Bound to %s:%d\n\n",
+ ast_inet_ntoa(oldsin.sin_addr),
+ ntohs(oldsin.sin_port));
+ else
+ ast_cli(fd, "Server Disabled\n\n");
+ ast_cli(fd, "Enabled URI's:\n");
+ ast_rwlock_rdlock(&uris_lock);
+ urih = uris;
+ while(urih){
+ ast_cli(fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
+ urih = urih->next;
+ }
+ if (!uris)
+ ast_cli(fd, "None.\n");
+ ast_rwlock_unlock(&uris_lock);
+
+ return RESULT_SUCCESS;
+}
+
+int ast_http_reload(void)
+{
+ return __ast_http_load(1);
+}
+
+static char show_http_help[] =
+"Usage: http show status\n"
+" Lists status of internal HTTP engine\n";
+
+static struct ast_cli_entry cli_http[] = {
+ { { "http", "show", "status", NULL },
+ handle_show_http, "Display HTTP server status",
+ show_http_help },
+};
+
+int ast_http_init(void)
+{
+ 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/main/image.c b/main/image.c
new file mode 100644
index 000000000..644758495
--- /dev/null
+++ b/main/image.c
@@ -0,0 +1,225 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "asterisk/sched.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/logger.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_LIST_HEAD_STATIC(imagers, ast_imager);
+
+int ast_image_register(struct ast_imager *img)
+{
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Registered format '%s' (%s)\n", img->name, img->desc);
+ AST_LIST_LOCK(&imagers);
+ AST_LIST_INSERT_HEAD(&imagers, img, list);
+ AST_LIST_UNLOCK(&imagers);
+ return 0;
+}
+
+void ast_image_unregister(struct ast_imager *img)
+{
+ struct ast_imager *i;
+
+ AST_LIST_LOCK(&imagers);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&imagers, i, list) {
+ if (i == img) {
+ AST_LIST_REMOVE_CURRENT(&imagers, list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&imagers);
+ if (i && (option_verbose > 1))
+ ast_verbose(VERBOSE_PREFIX_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_LIST_LOCK(&imagers);
+ AST_LIST_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_LIST_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 int show_image_formats_deprecated(int fd, int argc, char *argv[])
+{
+#define FORMAT "%10s %10s %50s %10s\n"
+#define FORMAT2 "%10s %10s %50s %10s\n"
+ struct ast_imager *i;
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ ast_cli(fd, FORMAT, "Name", "Extensions", "Description", "Format");
+ AST_LIST_TRAVERSE(&imagers, i, list)
+ ast_cli(fd, FORMAT2, i->name, i->exts, i->desc, ast_getformatname(i->format));
+ return RESULT_SUCCESS;
+}
+
+static int show_image_formats(int fd, int argc, char *argv[])
+{
+#define FORMAT "%10s %10s %50s %10s\n"
+#define FORMAT2 "%10s %10s %50s %10s\n"
+ struct ast_imager *i;
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ ast_cli(fd, FORMAT, "Name", "Extensions", "Description", "Format");
+ AST_LIST_TRAVERSE(&imagers, i, list)
+ ast_cli(fd, FORMAT2, i->name, i->exts, i->desc, ast_getformatname(i->format));
+ return RESULT_SUCCESS;
+}
+
+struct ast_cli_entry cli_show_image_formats_deprecated = {
+ { "show", "image", "formats" },
+ show_image_formats_deprecated, NULL,
+ NULL };
+
+struct ast_cli_entry cli_image[] = {
+ { { "core", "show", "image", "formats" },
+ show_image_formats, "Displays image formats",
+ "Usage: core show image formats\n"
+ " displays currently registered image formats (if any)\n", NULL, &cli_show_image_formats_deprecated },
+};
+
+int ast_image_init(void)
+{
+ ast_cli_register_multiple(cli_image, sizeof(cli_image) / sizeof(struct ast_cli_entry));
+ return 0;
+}
diff --git a/main/indications.c b/main/indications.c
new file mode 100644
index 000000000..64f96b5d5
--- /dev/null
+++ b/main/indications.c
@@ -0,0 +1,597 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "asterisk/indications.h"
+#include "asterisk/frame.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/logger.h"
+#include "asterisk/lock.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) free(ps->items);
+ free(ps);
+}
+
+static void * playtones_alloc(struct ast_channel *chan, void *params)
+{
+ struct playtones_def *pd = params;
+ struct playtones_state *ps;
+ 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)) {
+ free(d.items);
+ return -1;
+ }
+ return 0;
+}
+
+void ast_playtones_stop(struct ast_channel *chan)
+{
+ ast_deactivate_generator(chan);
+}
+
+/*--------------------------------------------*/
+
+static struct tone_zone *tone_zones;
+static struct tone_zone *current_tonezone;
+
+/* Protect the tone_zones list (highly unlikely that two things would change
+ * it at the same time, but still! */
+AST_MUTEX_DEFINE_STATIC(tzlock);
+
+struct tone_zone *ast_walk_indications(const struct tone_zone *cur)
+{
+ struct tone_zone *tz;
+
+ if (cur == NULL)
+ return tone_zones;
+ ast_mutex_lock(&tzlock);
+ for (tz = tone_zones; tz; tz = tz->next)
+ if (tz == cur)
+ break;
+ if (tz)
+ tz = tz->next;
+ ast_mutex_unlock(&tzlock);
+ return tz;
+}
+
+/* Set global indication country */
+int ast_set_indication_country(const char *country)
+{
+ if (country) {
+ struct tone_zone *z = ast_get_indication_zone(country);
+ if (z) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Setting default indication country to '%s'\n",country);
+ current_tonezone = z;
+ return 0;
+ }
+ }
+ return 1; /* not found */
+}
+
+/* locate tone_zone, given the country. if country == NULL, use the default country */
+struct tone_zone *ast_get_indication_zone(const char *country)
+{
+ struct tone_zone *tz;
+ int alias_loop = 0;
+
+ if (ast_strlen_zero(country)) {
+ /* No country specified? Return the default or the first in the list */
+ return current_tonezone ? current_tonezone : tone_zones;
+ }
+
+ ast_mutex_lock(&tzlock);
+ do {
+ for (tz=tone_zones; tz; tz=tz->next) {
+ if (strcasecmp(country,tz->country)==0) {
+ /* tone_zone found */
+ if (tz->alias && tz->alias[0]) {
+ country = tz->alias;
+ break;
+ }
+ ast_mutex_unlock(&tzlock);
+ return tz;
+ }
+ }
+ } while (++alias_loop<20 && tz);
+ ast_mutex_unlock(&tzlock);
+ if (alias_loop==20)
+ ast_log(LOG_NOTICE,"Alias loop for '%s' forcefull broken\n",country);
+ /* nothing found, sorry */
+ return 0;
+}
+
+/* locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */
+struct tone_zone_sound *ast_get_indication_tone(const struct tone_zone *zone, const char *indication)
+{
+ struct tone_zone_sound *ts;
+
+ /* we need some tonezone, pick the first */
+ if (zone == NULL && current_tonezone)
+ zone = current_tonezone; /* default country? */
+ if (zone == NULL && tone_zones)
+ zone = tone_zones; /* any country? */
+ if (zone == NULL)
+ return 0; /* not a single country insight */
+
+ ast_mutex_lock(&tzlock);
+ for (ts=zone->tones; ts; ts=ts->next) {
+ if (strcasecmp(indication,ts->name)==0) {
+ /* found indication! */
+ ast_mutex_unlock(&tzlock);
+ return ts;
+ }
+ }
+ /* nothing found, sorry */
+ ast_mutex_unlock(&tzlock);
+ return 0;
+}
+
+/* helper function to delete a tone_zone in its entirety */
+static inline void free_zone(struct tone_zone* zone)
+{
+ while (zone->tones) {
+ struct tone_zone_sound *tmp = zone->tones->next;
+ free((void*)zone->tones->name);
+ free((void*)zone->tones->data);
+ free(zone->tones);
+ zone->tones = tmp;
+ }
+ if (zone->ringcadence)
+ free(zone->ringcadence);
+ free(zone);
+}
+
+/*--------------------------------------------*/
+
+/* add a new country, if country exists, it will be replaced. */
+int ast_register_indication_country(struct tone_zone *zone)
+{
+ struct tone_zone *tz,*pz;
+
+ ast_mutex_lock(&tzlock);
+ for (pz=NULL,tz=tone_zones; tz; pz=tz,tz=tz->next) {
+ if (strcasecmp(zone->country,tz->country)==0) {
+ /* tone_zone already there, replace */
+ zone->next = tz->next;
+ if (pz)
+ pz->next = zone;
+ else
+ tone_zones = zone;
+ /* if we are replacing the default zone, re-point it */
+ if (tz == current_tonezone)
+ current_tonezone = zone;
+ /* now free the previous zone */
+ free_zone(tz);
+ ast_mutex_unlock(&tzlock);
+ return 0;
+ }
+ }
+ /* country not there, add */
+ zone->next = NULL;
+ if (pz)
+ pz->next = zone;
+ else
+ tone_zones = zone;
+ ast_mutex_unlock(&tzlock);
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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 tone_zone *tz, *pz = NULL, *tmp;
+ int res = -1;
+
+ ast_mutex_lock(&tzlock);
+ tz = tone_zones;
+ while (tz) {
+ if (country==NULL ||
+ (strcasecmp(country, tz->country)==0 ||
+ strcasecmp(country, tz->alias)==0)) {
+ /* tone_zone found, remove */
+ tmp = tz->next;
+ if (pz)
+ pz->next = tmp;
+ else
+ tone_zones = tmp;
+ /* if we are unregistering the default country, w'll notice */
+ if (tz == current_tonezone) {
+ ast_log(LOG_NOTICE,"Removed default indication country '%s'\n",tz->country);
+ current_tonezone = NULL;
+ }
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Unregistered indication country '%s'\n",tz->country);
+ free_zone(tz);
+ if (tone_zones == tz)
+ tone_zones = tmp;
+ tz = tmp;
+ res = 0;
+ }
+ else {
+ /* next zone please */
+ pz = tz;
+ tz = tz->next;
+ }
+ }
+ ast_mutex_unlock(&tzlock);
+ 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 tone_zone *zone, const char *indication, const char *tonelist)
+{
+ struct tone_zone_sound *ts,*ps;
+
+ /* is it an alias? stop */
+ if (zone->alias[0])
+ return -1;
+
+ ast_mutex_lock(&tzlock);
+ for (ps=NULL,ts=zone->tones; ts; ps=ts,ts=ts->next) {
+ if (strcasecmp(indication,ts->name)==0) {
+ /* indication already there, replace */
+ free((void*)ts->name);
+ free((void*)ts->data);
+ break;
+ }
+ }
+ if (!ts) {
+ /* not there, we have to add */
+ if (!(ts = ast_malloc(sizeof(*ts)))) {
+ ast_mutex_unlock(&tzlock);
+ return -2;
+ }
+ ts->next = NULL;
+ }
+ if (!(ts->name = ast_strdup(indication)) || !(ts->data = ast_strdup(tonelist))) {
+ ast_mutex_unlock(&tzlock);
+ return -2;
+ }
+ if (ps)
+ ps->next = ts;
+ else
+ zone->tones = ts;
+ ast_mutex_unlock(&tzlock);
+ return 0;
+}
+
+/* remove an existing country's indication. Both country and indication must exist */
+int ast_unregister_indication(struct tone_zone *zone, const char *indication)
+{
+ struct tone_zone_sound *ts,*ps = NULL, *tmp;
+ int res = -1;
+
+ /* is it an alias? stop */
+ if (zone->alias[0])
+ return -1;
+
+ ast_mutex_lock(&tzlock);
+ 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;
+ free((void*)ts->name);
+ free((void*)ts->data);
+ free(ts);
+ ts = tmp;
+ res = 0;
+ }
+ else {
+ /* next zone please */
+ ps = ts;
+ ts = ts->next;
+ }
+ }
+ /* indication not found, goodbye */
+ ast_mutex_unlock(&tzlock);
+ return res;
+}
diff --git a/main/io.c b/main/io.c
new file mode 100644
index 000000000..ced692180
--- /dev/null
+++ b/main/io.c
@@ -0,0 +1,371 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "asterisk/io.h"
+#include "asterisk/logger.h"
+#include "asterisk/utils.h"
+
+#ifdef DEBUG_IO
+#define DEBUG DEBUG_M
+#else
+#define DEBUG(a)
+#endif
+
+/*
+ * 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
+
+/* Global variables are now in a struct in order to be
+ made threadsafe */
+struct io_context {
+ /* Poll structure */
+ struct pollfd *fds;
+ /* Associated I/O records */
+ struct io_rec *ior;
+ /* First available fd */
+ unsigned int fdcnt;
+ /* Maximum available fd */
+ unsigned int maxfdcnt;
+ /* Currently used io callback */
+ int current_ioc;
+ /* Whether something has been deleted */
+ int needshrink;
+};
+
+struct io_context *io_context_create(void)
+{
+ /* Create an I/O context */
+ struct io_context *tmp;
+ if ((tmp = ast_malloc(sizeof(*tmp)))) {
+ 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)))) {
+ free(tmp);
+ tmp = NULL;
+ } else {
+ if (!(tmp->ior = ast_calloc(1, (GROW_SHRINK_SIZE / 2) * sizeof(*tmp->ior)))) {
+ free(tmp->fds);
+ 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)
+ free(ioc->fds);
+ if (ioc->ior)
+ free(ioc->ior);
+ free(ioc);
+}
+
+static int io_grow(struct io_context *ioc)
+{
+ /*
+ * Grow the size of our arrays. Return 0 on success or
+ * -1 on failure
+ */
+ void *tmp;
+ DEBUG(ast_log(LOG_DEBUG, "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;
+}
+
+int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
+{
+ /*
+ * Add a new I/O entry for this file descriptor
+ * with the given event mask, to call callback with
+ * data as an argument. Returns NULL on failure.
+ */
+ int *ret;
+ DEBUG(ast_log(LOG_DEBUG, "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 (*id < ioc->fdcnt) {
+ 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;
+ }
+ return NULL;
+}
+
+static int io_shrink(struct io_context *ioc)
+{
+ int getfrom;
+ int 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 */
+ 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;
+}
+
+int ast_io_wait(struct io_context *ioc, int howlong)
+{
+ /*
+ * Make the poll call, and call
+ * the callbacks for anything that needs
+ * to be handled
+ */
+ int res;
+ int x;
+ int origcnt;
+ DEBUG(ast_log(LOG_DEBUG, "ast_io_wait()\n"));
+ res = poll(ioc->fds, ioc->fdcnt, howlong);
+ if (res > 0) {
+ /*
+ * At least one event
+ */
+ 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_log(LOG_DEBUG, "Asterisk IO Dump: %d entries, %d max entries\n", ioc->fdcnt, ioc->maxfdcnt);
+ ast_log(LOG_DEBUG, "================================================\n");
+ ast_log(LOG_DEBUG, "| ID FD Callback Data Events |\n");
+ ast_log(LOG_DEBUG, "+------+------+-----------+-----------+--------+\n");
+ for (x = 0; x < ioc->fdcnt; x++) {
+ ast_log(LOG_DEBUG, "| %.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_log(LOG_DEBUG, "================================================\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/main/jitterbuf.c b/main/jitterbuf.c
new file mode 100644
index 000000000..849cffab5
--- /dev/null
+++ b/main/jitterbuf.c
@@ -0,0 +1,839 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#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 */
+ jb->info.current = jb->info.target = 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;
+ free(frame);
+ frame = next;
+ }
+
+ /* free ourselves! */
+ 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_log(LOG_DEBUG, "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_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 %ld to %ld\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_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_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_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;
+
+ return JB_OK;
+}
+
+
diff --git a/main/loader.c b/main/loader.c
new file mode 100644
index 000000000..60c8a7676
--- /dev/null
+++ b/main/loader.c
@@ -0,0 +1,1001 @@
+/*
+ * 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 <stdio.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+#include "asterisk/config.h"
+#include "asterisk/logger.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"
+
+#include <dlfcn.h>
+
+#include "asterisk/md5.h"
+#include "asterisk/utils.h"
+
+#ifndef RTLD_NOW
+#define RTLD_NOW 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);
+
+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_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);
+
+ if (!embedding)
+ 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(&module_list, entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&module_list);
+
+ if (mod) {
+ AST_LIST_HEAD_DESTROY(&mod->users);
+ 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);
+ 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);
+ 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_log(LOG_DEBUG, "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[256];
+ void *lib;
+ struct ast_module *mod;
+ char *resource = (char *) resource_in;
+ unsigned int wants_global;
+
+ if (strcasecmp(resource + strlen(resource) - 3, ".so")) {
+ resource = alloca(strlen(resource_in) + 3);
+ strcpy(resource, resource_in);
+ strcat(resource, ".so");
+ }
+
+ snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_MODULE_DIR, resource);
+
+ /* 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 */
+
+ if (!(resource_being_loaded = ast_calloc(1, sizeof(*resource_being_loaded) + strlen(resource) + 1)))
+ return NULL;
+
+ strcpy(resource_being_loaded->resource, resource);
+
+ if (!(lib = dlopen(fn, RTLD_LAZY | RTLD_LOCAL))) {
+ ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
+ 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));
+ free(resource_being_loaded);
+ return NULL;
+ }
+#else
+ while (!dlclose(lib));
+ resource_being_loaded = NULL;
+
+ /* start the load process again */
+
+ if (!(resource_being_loaded = ast_calloc(1, sizeof(*resource_being_loaded) + strlen(resource) + 1)))
+ return NULL;
+
+ strcpy(resource_being_loaded->resource, resource);
+
+ 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());
+ 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 (!mod->lib) {
+ ast_log(LOG_WARNING, "Unloading embedded modules is not supported.\n");
+ 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);
+
+#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 = 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 = 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 = time(NULL);
+
+ /* 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) {
+ if (!name)
+ continue;
+ ast_log(LOG_NOTICE, "The module '%s' was not properly initialized. "
+ "Before reloading the module, you must run \"module load %s\" "
+ "and fix whatever is preventing the module from being initialized.\n",
+ name, name);
+ res = 2; /* Don't report that the module was not found */
+ break;
+ }
+
+ if (!info->reload) { /* cannot be reloaded */
+ if (res < 1) /* store result if possible */
+ res = 1; /* 1 = no reload() method */
+ continue;
+ }
+
+ res = 2;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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_test_flag(mod->info, AST_MODFLAG_BUILDSUM)) {
+ ast_log(LOG_WARNING, "Module '%s' was not compiled against a recent version of Asterisk and may cause instability.\n", mod->resource);
+ } else 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;
+ }
+
+ mod->flags.declined = 0;
+
+ if (mod->info->load)
+ res = mod->info->load();
+
+ switch (res) {
+ case AST_MODULE_LOAD_SUCCESS:
+ if (!ast_fully_booted) {
+ if (option_verbose)
+ ast_verbose("%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 {
+ if (option_verbose)
+ ast_verbose(VERBOSE_PREFIX_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;
+}
+
+static int translate_module_name(char *oldname, char *newname)
+{
+ if (!strcasecmp(oldname, "app_zapbarge.so"))
+ ast_copy_string(newname, "app_dahdibarge.so", 18);
+ else if(!strcasecmp(oldname, "app_zapras.so"))
+ ast_copy_string(newname, "app_dahdiras.so", 16);
+ else if(!strcasecmp(oldname, "app_zapscan.so"))
+ ast_copy_string(newname, "app_dahdiscan.so", 17);
+ else if(!strcasecmp(oldname, "codec_zap.so"))
+ ast_copy_string(newname, "codec_dahdi.so", 16);
+ else
+ return -1; /* no use for newname, oldname is fine */
+
+ return 0;
+}
+
+
+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;
+
+ int translate_status;
+ char newname[18]; /* although this would normally be 80, max length in translate_module_name is 18 */
+#ifdef LOADABLE_MODULES
+ struct dirent *dirent;
+ DIR *dir;
+#endif
+
+ /* all embedded modules have registered themselves by now */
+ embedding = 0;
+
+ if (option_verbose)
+ ast_verbose("Asterisk Dynamic Loader Starting:\n");
+
+ AST_LIST_HEAD_INIT_NOLOCK(&load_order);
+
+ AST_LIST_LOCK(&module_list);
+
+ if (!(cfg = ast_config_load(AST_MODULE_CONFIG))) {
+ 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")) {
+ translate_status = translate_module_name(v->value, newname);
+ if (!translate_status)
+ ast_log(LOG_WARNING, "Use of old module name %s is deprecated, please use %s instead.\n", v->value, newname);
+ add_to_load_order(translate_status ? v->value : newname, &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) {
+ translate_status = translate_module_name(v->value, newname);
+ if (!resource_name_match(order->resource, translate_status ? v->value : newname)) {
+ if (!translate_status)
+ ast_log(LOG_WARNING, "Use of old module name %s is deprecated, please use %s instead.\n", v->value, newname);
+ AST_LIST_REMOVE_CURRENT(&load_order, entry);
+ free(order->resource);
+ 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:
+ case AST_MODULE_LOAD_DECLINE:
+ AST_LIST_REMOVE_CURRENT(&load_order, entry);
+ free(order->resource);
+ 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:
+ case AST_MODULE_LOAD_DECLINE:
+ AST_LIST_REMOVE_CURRENT(&load_order, entry);
+ free(order->resource);
+ 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))) {
+ free(order->resource);
+ free(order);
+ }
+
+ AST_LIST_UNLOCK(&module_list);
+
+ 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(&updaters);
+ AST_LIST_TRAVERSE(&updaters, m, entry)
+ m->updater();
+ AST_LIST_UNLOCK(&updaters);
+}
+
+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;
+}
+
+int ast_loader_register(int (*v)(void))
+{
+ struct loadupdate *tmp;
+
+ if (!(tmp = ast_malloc(sizeof(*tmp))))
+ return -1;
+
+ tmp->updater = v;
+ AST_LIST_LOCK(&updaters);
+ AST_LIST_INSERT_HEAD(&updaters, tmp, entry);
+ AST_LIST_UNLOCK(&updaters);
+
+ return 0;
+}
+
+int ast_loader_unregister(int (*v)(void))
+{
+ struct loadupdate *cur;
+
+ AST_LIST_LOCK(&updaters);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&updaters, cur, entry) {
+ if (cur->updater == v) {
+ AST_LIST_REMOVE_CURRENT(&updaters, entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&updaters);
+
+ 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/main/logger.c b/main/logger.c
new file mode 100644
index 000000000..0d1ef4b71
--- /dev/null
+++ b/main/logger.c
@@ -0,0 +1,939 @@
+/*
+ * 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>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.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)
+
+#include "asterisk/logger.h"
+#include "asterisk/lock.h"
+#include "asterisk/options.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"
+
+#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 int filesize_reload_needed;
+static int global_logmask = -1;
+
+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_LIST_HEAD_STATIC(logchannels, logchannel);
+
+static FILE *eventlog;
+static FILE *qlog;
+
+static char *levels[] = {
+ "DEBUG",
+ "EVENT",
+ "NOTICE",
+ "WARNING",
+ "ERROR",
+ "VERBOSE",
+ "DTMF"
+};
+
+static int colors[] = {
+ COLOR_BRGREEN,
+ COLOR_BRBLUE,
+ COLOR_YELLOW,
+ COLOR_BRRED,
+ COLOR_RED,
+ COLOR_GREEN,
+ COLOR_BRGREEN
+};
+
+AST_THREADSTORAGE(verbose_buf, verbose_buf_init);
+#define VERBOSE_BUF_INIT_SIZE 128
+
+AST_THREADSTORAGE(log_buf, log_buf_init);
+#define LOG_BUF_INIT_SIZE 128
+
+static int make_components(char *s, int lineno)
+{
+ char *w;
+ int res = 0;
+ char *stringp = 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(char *channel, 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");
+ 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",(char *)ast_config_AST_LOG_DIR, channel, hostname);
+ } else {
+ snprintf(chan->filename, sizeof(chan->filename), "%s/%s", (char *)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(void)
+{
+ struct logchannel *chan;
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ const char *s;
+
+ /* delete our list of log channels */
+ AST_LIST_LOCK(&logchannels);
+ while ((chan = AST_LIST_REMOVE_HEAD(&logchannels, list)))
+ free(chan);
+ AST_LIST_UNLOCK(&logchannels);
+
+ global_logmask = 0;
+ errno = 0;
+ /* close syslog */
+ closelog();
+
+ cfg = ast_config_load("logger.conf");
+
+ /* 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 */
+ AST_LIST_LOCK(&logchannels);
+ AST_LIST_INSERT_HEAD(&logchannels, chan, list);
+ AST_LIST_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));
+ ast_log(LOG_WARNING, "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);
+
+ AST_LIST_LOCK(&logchannels);
+ var = ast_variable_browse(cfg, "logfiles");
+ for (; var; var = var->next) {
+ if (!(chan = make_logchannel(var->name, var->value, var->lineno)))
+ continue;
+ AST_LIST_INSERT_HEAD(&logchannels, chan, list);
+ global_logmask |= chan->logmask;
+ }
+ AST_LIST_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;
+ AST_LIST_LOCK(&logchannels);
+ if (qlog) {
+ va_start(ap, fmt);
+ fprintf(qlog, "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
+ vfprintf(qlog, fmt, ap);
+ fprintf(qlog, "\n");
+ va_end(ap);
+ fflush(qlog);
+ }
+ AST_LIST_UNLOCK(&logchannels);
+}
+
+int reload_logger(int rotate)
+{
+ char old[PATH_MAX] = "";
+ char new[PATH_MAX];
+ int event_rotate = rotate, queue_rotate = rotate;
+ struct logchannel *f;
+ FILE *myf;
+ int x, res = 0;
+
+ AST_LIST_LOCK(&logchannels);
+
+ if (eventlog)
+ fclose(eventlog);
+ else
+ event_rotate = 0;
+ eventlog = NULL;
+
+ if (qlog)
+ fclose(qlog);
+ else
+ queue_rotate = 0;
+ qlog = NULL;
+
+ mkdir((char *)ast_config_AST_LOG_DIR, 0755);
+
+ AST_LIST_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) {
+ ast_copy_string(old, f->filename, sizeof(old));
+
+ for (x = 0; ; x++) {
+ snprintf(new, sizeof(new), "%s.%d", f->filename, x);
+ myf = fopen((char *)new, "r");
+ if (myf)
+ fclose(myf);
+ else
+ break;
+ }
+
+ /* do it */
+ if (rename(old,new))
+ fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
+ }
+ }
+ }
+
+ filesize_reload_needed = 0;
+
+ init_logger_chain();
+
+ if (logfiles.event_log) {
+ snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
+ if (event_rotate) {
+ for (x=0;;x++) {
+ snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, EVENTLOG,x);
+ myf = fopen((char *)new, "r");
+ if (myf) /* File exists */
+ fclose(myf);
+ else
+ break;
+ }
+
+ /* do it */
+ if (rename(old,new))
+ ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
+ }
+
+ eventlog = fopen(old, "a");
+ if (eventlog) {
+ ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
+ if (option_verbose)
+ ast_verbose("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", (char *)ast_config_AST_LOG_DIR, QUEUELOG);
+ if (queue_rotate) {
+ for (x = 0; ; x++) {
+ snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, QUEUELOG, x);
+ myf = fopen((char *)new, "r");
+ if (myf) /* File exists */
+ fclose(myf);
+ else
+ break;
+ }
+
+ /* do it */
+ if (rename(old, new))
+ ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
+ }
+
+ qlog = fopen(old, "a");
+ if (qlog) {
+ ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
+ ast_log(LOG_EVENT, "Restarted Asterisk Queue Logger\n");
+ if (option_verbose)
+ ast_verbose("Asterisk Queue Logger restarted\n");
+ } else {
+ ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
+ res = -1;
+ }
+ }
+
+ AST_LIST_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 int handle_logger_reload(int fd, int argc, char *argv[])
+{
+ int result = logger_reload();
+ if (result == RESULT_FAILURE)
+ ast_cli(fd, "Failed to reload the logger\n");
+ return result;
+}
+
+static int handle_logger_rotate(int fd, int argc, char *argv[])
+{
+ if(reload_logger(1)) {
+ ast_cli(fd, "Failed to reload the logger and rotate log files\n");
+ return RESULT_FAILURE;
+ }
+ return RESULT_SUCCESS;
+}
+
+/*! \brief CLI command to show logging system configuration */
+static int handle_logger_show_channels(int fd, int argc, char *argv[])
+{
+#define FORMATL "%-35.35s %-8.8s %-9.9s "
+ struct logchannel *chan;
+
+ ast_cli(fd,FORMATL, "Channel", "Type", "Status");
+ ast_cli(fd, "Configuration\n");
+ ast_cli(fd,FORMATL, "-------", "----", "------");
+ ast_cli(fd, "-------------\n");
+ AST_LIST_LOCK(&logchannels);
+ AST_LIST_TRAVERSE(&logchannels, chan, list) {
+ ast_cli(fd, FORMATL, chan->filename, chan->type==LOGTYPE_CONSOLE ? "Console" : (chan->type==LOGTYPE_SYSLOG ? "Syslog" : "File"),
+ chan->disabled ? "Disabled" : "Enabled");
+ ast_cli(fd, " - ");
+ if (chan->logmask & (1 << __LOG_DEBUG))
+ ast_cli(fd, "Debug ");
+ if (chan->logmask & (1 << __LOG_DTMF))
+ ast_cli(fd, "DTMF ");
+ if (chan->logmask & (1 << __LOG_VERBOSE))
+ ast_cli(fd, "Verbose ");
+ if (chan->logmask & (1 << __LOG_WARNING))
+ ast_cli(fd, "Warning ");
+ if (chan->logmask & (1 << __LOG_NOTICE))
+ ast_cli(fd, "Notice ");
+ if (chan->logmask & (1 << __LOG_ERROR))
+ ast_cli(fd, "Error ");
+ if (chan->logmask & (1 << __LOG_EVENT))
+ ast_cli(fd, "Event ");
+ ast_cli(fd, "\n");
+ }
+ AST_LIST_UNLOCK(&logchannels);
+ ast_cli(fd, "\n");
+
+ return RESULT_SUCCESS;
+}
+
+struct verb {
+ void (*verboser)(const char *string);
+ AST_LIST_ENTRY(verb) list;
+};
+
+static AST_LIST_HEAD_STATIC(verbosers, verb);
+
+static char logger_reload_help[] =
+"Usage: logger reload\n"
+" Reloads the logger subsystem state. Use after restarting syslogd(8) if you are using syslog logging.\n";
+
+static char logger_rotate_help[] =
+"Usage: logger rotate\n"
+" Rotates and Reopens the log files.\n";
+
+static char logger_show_channels_help[] =
+"Usage: logger show channels\n"
+" List configured logger channels.\n";
+
+static struct ast_cli_entry cli_logger[] = {
+ { { "logger", "show", "channels", NULL },
+ handle_logger_show_channels, "List configured log channels",
+ logger_show_channels_help },
+
+ { { "logger", "reload", NULL },
+ handle_logger_reload, "Reopens the log files",
+ logger_reload_help },
+
+ { { "logger", "rotate", NULL },
+ handle_logger_rotate, "Rotates and reopens the log files",
+ logger_rotate_help },
+};
+
+static int handle_SIGXFSZ(int sig)
+{
+ /* Indicate need to reload */
+ filesize_reload_needed = 1;
+ return 0;
+}
+
+int init_logger(void)
+{
+ char tmp[256];
+ int res = 0;
+
+ /* auto rotate if sig SIGXFSZ comes a-knockin */
+ (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
+
+ /* register the logger cli commands */
+ ast_cli_register_multiple(cli_logger, sizeof(cli_logger) / sizeof(struct ast_cli_entry));
+
+ mkdir((char *)ast_config_AST_LOG_DIR, 0755);
+
+ /* create log channels */
+ init_logger_chain();
+
+ /* create the eventlog */
+ if (logfiles.event_log) {
+ mkdir((char *)ast_config_AST_LOG_DIR, 0755);
+ snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
+ eventlog = fopen((char *)tmp, "a");
+ if (eventlog) {
+ ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
+ if (option_verbose)
+ ast_verbose("Asterisk Event Logger Started %s\n",(char *)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", (char *)ast_config_AST_LOG_DIR, QUEUELOG);
+ qlog = fopen(tmp, "a");
+ ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
+ }
+ return res;
+}
+
+void close_logger(void)
+{
+ struct logchannel *f;
+
+ AST_LIST_LOCK(&logchannels);
+
+ if (eventlog) {
+ fclose(eventlog);
+ eventlog = NULL;
+ }
+
+ if (qlog) {
+ fclose(qlog);
+ qlog = NULL;
+ }
+
+ AST_LIST_TRAVERSE(&logchannels, f, list) {
+ if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
+ fclose(f->fileptr);
+ f->fileptr = NULL;
+ }
+ }
+
+ closelog(); /* syslog */
+
+ AST_LIST_UNLOCK(&logchannels);
+
+ return;
+}
+
+static void __attribute__((format(printf, 5, 0))) ast_log_vsyslog(int level, const char *file, int line, const char *function, const char *fmt, va_list args)
+{
+ char buf[BUFSIZ];
+ char *s;
+
+ 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]: ", (long)GETTID());
+ level = __LOG_DEBUG;
+ } else if (level == __LOG_DTMF) {
+ snprintf(buf, sizeof(buf), "DTMF[%ld]: ", (long)GETTID());
+ level = __LOG_DEBUG;
+ } else {
+ snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: ",
+ levels[level], (long)GETTID(), file, line, function);
+ }
+ s = buf + strlen(buf);
+ vsnprintf(s, sizeof(buf) - strlen(buf), fmt, args);
+ term_strip(s, s, strlen(s) + 1);
+ syslog(syslog_level_map[level], "%s", buf);
+}
+
+/*!
+ * \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 logchannel *chan;
+ struct ast_dynamic_str *buf;
+ time_t t;
+ struct tm tm;
+ char date[256];
+
+ va_list ap;
+
+ if (!(buf = ast_dynamic_str_thread_get(&log_buf, LOG_BUF_INIT_SIZE)))
+ return;
+
+ if (AST_LIST_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_dynamic_str_thread_set_va(&buf, BUFSIZ, &log_buf, fmt, ap);
+ 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;
+
+ /* Ignore anything other than the currently debugged file if there is one */
+ if ((level == __LOG_DEBUG) && !ast_strlen_zero(debug_filename) && strcasecmp(debug_filename, file))
+ return;
+
+ time(&t);
+ ast_localtime(&t, &tm, NULL);
+ strftime(date, sizeof(date), dateformat, &tm);
+
+ AST_LIST_LOCK(&logchannels);
+
+ if (logfiles.event_log && level == __LOG_EVENT) {
+ va_start(ap, fmt);
+
+ fprintf(eventlog, "%s asterisk[%ld]: ", date, (long)getpid());
+ vfprintf(eventlog, fmt, ap);
+ fflush(eventlog);
+
+ va_end(ap);
+ AST_LIST_UNLOCK(&logchannels);
+ return;
+ }
+
+ AST_LIST_TRAVERSE(&logchannels, chan, list) {
+ if (chan->disabled)
+ break;
+ /* Check syslog channels */
+ if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << level))) {
+ va_start(ap, fmt);
+ ast_log_vsyslog(level, file, line, function, fmt, ap);
+ va_end(ap);
+ /* Console channels */
+ } else if ((chan->logmask & (1 << level)) && (chan->type == LOGTYPE_CONSOLE)) {
+ char linestr[128];
+ char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
+
+ if (level != __LOG_VERBOSE) {
+ int res;
+ sprintf(linestr, "%d", line);
+ ast_dynamic_str_thread_set(&buf, BUFSIZ, &log_buf,
+ "[%s] %s[%ld]: %s:%s %s: ",
+ date,
+ term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
+ (long)GETTID(),
+ term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
+ term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
+ term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
+ /*filter to the console!*/
+ term_filter_escapes(buf->str);
+ ast_console_puts_mutable(buf->str);
+
+ va_start(ap, fmt);
+ res = ast_dynamic_str_thread_set_va(&buf, BUFSIZ, &log_buf, fmt, ap);
+ va_end(ap);
+ if (res != AST_DYNSTR_BUILD_FAILED)
+ ast_console_puts_mutable(buf->str);
+ }
+ /* File channels */
+ } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) {
+ int res;
+ ast_dynamic_str_thread_set(&buf, BUFSIZ, &log_buf,
+ "[%s] %s[%ld] %s: ",
+ date, levels[level], (long)GETTID(), file);
+ res = fprintf(chan->fileptr, "%s", buf->str);
+ if (res <= 0 && !ast_strlen_zero(buf->str)) { /* Error, no characters printed */
+ 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 {
+ int res;
+ /* No error message, continue printing */
+ va_start(ap, fmt);
+ res = ast_dynamic_str_thread_set_va(&buf, BUFSIZ, &log_buf, fmt, ap);
+ va_end(ap);
+ if (res != AST_DYNSTR_BUILD_FAILED) {
+ term_strip(buf->str, buf->str, buf->len);
+ fputs(buf->str, chan->fileptr);
+ fflush(chan->fileptr);
+ }
+ }
+ }
+ }
+
+ AST_LIST_UNLOCK(&logchannels);
+
+ 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");
+ }
+}
+
+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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "Could not allocate memory for backtrace\n");
+ }
+ 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 verb *v;
+ struct ast_dynamic_str *buf;
+ int res;
+ va_list ap;
+
+ if (ast_opt_timestamp) {
+ time_t t;
+ struct tm tm;
+ char date[40];
+ char *datefmt;
+
+ time(&t);
+ ast_localtime(&t, &tm, NULL);
+ strftime(date, sizeof(date), dateformat, &tm);
+ datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1);
+ sprintf(datefmt, "%c[%s] %s", 127, date, fmt);
+ fmt = datefmt;
+ } else {
+ char *tmp = alloca(strlen(fmt) + 2);
+ sprintf(tmp, "%c%s", 127, fmt);
+ fmt = tmp;
+ }
+
+ if (!(buf = ast_dynamic_str_thread_get(&verbose_buf, VERBOSE_BUF_INIT_SIZE)))
+ return;
+
+ va_start(ap, fmt);
+ res = ast_dynamic_str_thread_set_va(&buf, 0, &verbose_buf, fmt, ap);
+ va_end(ap);
+
+ if (res == AST_DYNSTR_BUILD_FAILED)
+ return;
+
+ /* filter out possibly hazardous escape sequences */
+ term_filter_escapes(buf->str);
+
+ AST_LIST_LOCK(&verbosers);
+ AST_LIST_TRAVERSE(&verbosers, v, list)
+ v->verboser(buf->str);
+ AST_LIST_UNLOCK(&verbosers);
+
+ ast_log(LOG_VERBOSE, "%s", buf->str + 1);
+}
+
+int ast_register_verbose(void (*v)(const char *string))
+{
+ struct verb *verb;
+
+ if (!(verb = ast_malloc(sizeof(*verb))))
+ return -1;
+
+ verb->verboser = v;
+
+ AST_LIST_LOCK(&verbosers);
+ AST_LIST_INSERT_HEAD(&verbosers, verb, list);
+ AST_LIST_UNLOCK(&verbosers);
+
+ return 0;
+}
+
+int ast_unregister_verbose(void (*v)(const char *string))
+{
+ struct verb *cur;
+
+ AST_LIST_LOCK(&verbosers);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&verbosers, cur, list) {
+ if (cur->verboser == v) {
+ AST_LIST_REMOVE_CURRENT(&verbosers, list);
+ free(cur);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&verbosers);
+
+ return cur ? 0 : -1;
+}
diff --git a/main/manager.c b/main/manager.c
new file mode 100644
index 000000000..c0245ad2a
--- /dev/null
+++ b/main/manager.c
@@ -0,0 +1,3194 @@
+/*
+ * 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>
+ *
+ * Channel Management and more
+ *
+ * \ref amiconf
+ */
+
+/*! \addtogroup Group_AMI AMI functions
+*/
+/*! @{
+ Doxygen group */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/manager.h"
+#include "asterisk/config.h"
+#include "asterisk/callerid.h"
+#include "asterisk/lock.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.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/http.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/term.h"
+#include "asterisk/astobj2.h"
+
+struct fast_originate_helper {
+ char tech[AST_MAX_EXTENSION];
+ /*! data can contain a channel name, extension number, username, password, etc. */
+ char data[512];
+ int timeout;
+ int format;
+ 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;
+};
+
+struct eventqent {
+ int usecount;
+ int category;
+ struct eventqent *next;
+ char eventdata[1];
+};
+
+static int enabled;
+static int portno = DEFAULT_MANAGER_PORT;
+static int asock = -1;
+static int displayconnects = 1;
+static int timestampevents;
+static int httptimeout = 60;
+
+static pthread_t t;
+static int block_sockets;
+static int num_sessions;
+
+/* Protected by the sessions list lock */
+struct eventqent *master_eventq = NULL;
+
+AST_THREADSTORAGE(manager_event_buf, manager_event_buf_init);
+#define MANAGER_EVENT_BUF_INITSIZE 256
+
+AST_THREADSTORAGE(astman_append_buf, astman_append_buf_init);
+#define ASTMAN_APPEND_BUF_INITSIZE 256
+
+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" },
+ { -1, "all" },
+ { 0, "none" },
+};
+
+#define MAX_BLACKLIST_CMD_LEN 2
+static struct {
+ char *words[AST_MAX_CMD_LEN];
+} command_blacklist[] = {
+ {{ "module", "load", NULL }},
+ {{ "module", "unload", NULL }},
+ {{ "restart", "gracefully", NULL }},
+};
+
+struct mansession {
+ /*! Execution thread */
+ pthread_t t;
+ /*! Thread lock -- don't use in action callbacks, it's already taken care of */
+ ast_mutex_t __lock;
+ /*! socket address */
+ struct sockaddr_in sin;
+ /*! TCP socket */
+ int fd;
+ /*! Whether an HTTP manager is in use */
+ int inuse;
+ /*! Whether an HTTP session should be destroyed */
+ int needdestroy;
+ /*! Whether an HTTP session has someone waiting on events */
+ pthread_t waiting_thread;
+ /*! Unique manager identifer */
+ uint32_t managerid;
+ /*! Session timeout if HTTP */
+ time_t sessiontimeout;
+ /*! Output from manager interface */
+ struct ast_dynamic_str *outputstr;
+ /*! Logged in username */
+ char username[80];
+ /*! Authentication challenge */
+ char challenge[10];
+ /*! Authentication status */
+ int authenticated;
+ /*! Authorization for reading */
+ int readperm;
+ /*! Authorization for writing */
+ int writeperm;
+ /*! Buffer */
+ char inbuf[1024];
+ int inlen;
+ int send_events;
+ int displaysystemname; /*!< Add system name to manager responses and events */
+ /* Queued events that we've not had the ability to send yet */
+ struct eventqent *eventq;
+ /* Timeout for ast_carefulwrite() */
+ int writetimeout;
+ int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
+ AST_LIST_ENTRY(mansession) list;
+};
+
+static AST_LIST_HEAD_STATIC(sessions, mansession);
+
+struct ast_manager_user {
+ char username[80];
+ char *secret;
+ char *deny;
+ char *permit;
+ char *read;
+ char *write;
+ unsigned int displayconnects:1;
+ int keep;
+ AST_LIST_ENTRY(ast_manager_user) list;
+};
+
+static AST_LIST_HEAD_STATIC(users, ast_manager_user);
+
+static struct manager_action *first_action;
+AST_RWLOCK_DEFINE_STATIC(actionlock);
+
+/*! \brief Convert authority code to string with serveral options */
+static char *authority_to_str(int authority, char *res, int reslen)
+{
+ int running_total = 0, i;
+
+ memset(res, 0, reslen);
+ for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
+ if (authority & perms[i].num) {
+ if (*res) {
+ strncat(res, ",", (reslen > running_total) ? reslen - running_total - 1 : 0);
+ running_total++;
+ }
+ strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total - 1 : 0);
+ running_total += strlen(perms[i].label);
+ }
+ }
+
+ if (ast_strlen_zero(res))
+ ast_copy_string(res, "<none>", reslen);
+
+ return res;
+}
+
+static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
+{
+ struct manager_action *cur;
+ int which = 0;
+ char *ret = NULL;
+
+ ast_rwlock_rdlock(&actionlock);
+ for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
+ if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
+ ret = ast_strdup(cur->action);
+ break; /* make sure we exit even if ast_strdup() returns NULL */
+ }
+ }
+ ast_rwlock_unlock(&actionlock);
+
+ return ret;
+}
+
+static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
+{
+ while (*src && (*maxlen > 6)) {
+ switch (*src) {
+ case '<':
+ strcpy(*dst, "&lt;");
+ (*dst) += 4;
+ *maxlen -= 4;
+ break;
+ case '>':
+ strcpy(*dst, "&gt;");
+ (*dst) += 4;
+ *maxlen -= 4;
+ break;
+ case '\"':
+ strcpy(*dst, "&quot;");
+ (*dst) += 6;
+ *maxlen -= 6;
+ break;
+ case '\'':
+ strcpy(*dst, "&apos;");
+ (*dst) += 6;
+ *maxlen -= 6;
+ break;
+ case '&':
+ strcpy(*dst, "&amp;");
+ (*dst) += 5;
+ *maxlen -= 5;
+ break;
+ default:
+ *(*dst)++ = lower ? tolower(*src) : *src;
+ (*maxlen)--;
+ }
+ src++;
+ }
+}
+
+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 | CMP_STOP : 0;
+}
+
+static char *xml_translate(char *in, struct ast_variable *vars)
+{
+ struct ast_variable *v;
+ char *dest = NULL;
+ char *out, *tmp, *var, *val;
+ char *objtype = NULL;
+ int colons = 0;
+ int breaks = 0;
+ size_t len;
+ int count = 1;
+ int escaped = 0;
+ int inobj = 0;
+ int x;
+ 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";
+ for (x = 0; in[x]; x++) {
+ if (in[x] == ':')
+ colons++;
+ else if (in[x] == '\n')
+ breaks++;
+ else if (strchr("&\"<>\'", in[x]))
+ escaped++;
+ }
+ len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
+ out = ast_malloc(len);
+ if (!out)
+ return 0;
+ tmp = out;
+ while (*in) {
+ var = in;
+ while (*in && (*in >= 32))
+ in++;
+ if (*in) {
+ if ((count > 3) && inobj) {
+ ast_build_string(&tmp, &len, " /></response>\n");
+ inobj = 0;
+
+ /* Entity is closed, so close out the name cache */
+ ao2_ref(vco, -1);
+ vco = NULL;
+ }
+ count = 0;
+ while (*in && (*in < 32)) {
+ *in = '\0';
+ in++;
+ count++;
+ }
+ val = strchr(var, ':');
+ if (val) {
+ *val = '\0';
+ val++;
+ if (*val == ' ')
+ val++;
+ if (!inobj) {
+ vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
+ ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
+ inobj = 1;
+ }
+
+ /* Check if the var has been used already */
+ 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);
+ }
+
+ ast_build_string(&tmp, &len, " ");
+ xml_copy_escape(&tmp, &len, var, 1);
+ if (vc->count > 1)
+ ast_build_string(&tmp, &len, "-%d", vc->count);
+ ast_build_string(&tmp, &len, "='");
+ xml_copy_escape(&tmp, &len, val, 0);
+ ast_build_string(&tmp, &len, "'");
+ ao2_ref(vc, -1);
+ }
+ }
+ }
+ if (inobj)
+ ast_build_string(&tmp, &len, " /></response>\n");
+ if (vco)
+ ao2_ref(vco, -1);
+ return out;
+}
+
+static char *html_translate(char *in)
+{
+ int x;
+ int colons = 0;
+ int breaks = 0;
+ size_t len;
+ int count = 1;
+ char *tmp, *var, *val, *out;
+
+ for (x=0; in[x]; x++) {
+ if (in[x] == ':')
+ colons++;
+ if (in[x] == '\n')
+ breaks++;
+ }
+ len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
+ out = ast_malloc(len);
+ if (!out)
+ return 0;
+ tmp = out;
+ while (*in) {
+ var = in;
+ while (*in && (*in >= 32))
+ in++;
+ if (*in) {
+ if ((count % 4) == 0){
+ ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+ }
+ count = 0;
+ while (*in && (*in < 32)) {
+ *in = '\0';
+ in++;
+ count++;
+ }
+ val = strchr(var, ':');
+ if (val) {
+ *val = '\0';
+ val++;
+ if (*val == ' ')
+ val++;
+ ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
+ }
+ }
+ }
+ return out;
+}
+
+
+
+static struct ast_manager_user *ast_get_manager_by_name_locked(const char *name)
+{
+ struct ast_manager_user *user = NULL;
+
+ AST_LIST_TRAVERSE(&users, user, list)
+ if (!strcasecmp(user->username, name))
+ break;
+ return user;
+}
+
+void astman_append(struct mansession *s, const char *fmt, ...)
+{
+ va_list ap;
+ struct ast_dynamic_str *buf;
+
+ ast_mutex_lock(&s->__lock);
+
+ if (!(buf = ast_dynamic_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
+ ast_mutex_unlock(&s->__lock);
+ return;
+ }
+
+ va_start(ap, fmt);
+ ast_dynamic_str_thread_set_va(&buf, 0, &astman_append_buf, fmt, ap);
+ va_end(ap);
+
+ if (s->fd > -1)
+ ast_carefulwrite(s->fd, buf->str, strlen(buf->str), s->writetimeout);
+ else {
+ if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr)))) {
+ ast_mutex_unlock(&s->__lock);
+ return;
+ }
+
+ ast_dynamic_str_append(&s->outputstr, 0, "%s", buf->str);
+ }
+
+ ast_mutex_unlock(&s->__lock);
+}
+
+static int handle_showmancmd(int fd, int argc, char *argv[])
+{
+ struct manager_action *cur;
+ char authority[80];
+ int num;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ ast_rwlock_rdlock(&actionlock);
+ for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
+ for (num = 3; num < argc; num++) {
+ if (!strcasecmp(cur->action, argv[num])) {
+ ast_cli(fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n", cur->action, cur->synopsis, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->description ? cur->description : "");
+ }
+ }
+ }
+ ast_rwlock_unlock(&actionlock);
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_showmanager(int fd, int argc, char *argv[])
+{
+ struct ast_manager_user *user = NULL;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ AST_LIST_LOCK(&users);
+
+ if (!(user = ast_get_manager_by_name_locked(argv[3]))) {
+ ast_cli(fd, "There is no manager called %s\n", argv[3]);
+ AST_LIST_UNLOCK(&users);
+ return -1;
+ }
+
+ ast_cli(fd,"\n");
+ ast_cli(fd,
+ " username: %s\n"
+ " secret: %s\n"
+ " deny: %s\n"
+ " permit: %s\n"
+ " read: %s\n"
+ " write: %s\n"
+ "displayconnects: %s\n",
+ (user->username ? user->username : "(N/A)"),
+ (user->secret ? "<Set>" : "(N/A)"),
+ (user->deny ? user->deny : "(N/A)"),
+ (user->permit ? user->permit : "(N/A)"),
+ (user->read ? user->read : "(N/A)"),
+ (user->write ? user->write : "(N/A)"),
+ (user->displayconnects ? "yes" : "no"));
+
+ AST_LIST_UNLOCK(&users);
+
+ return RESULT_SUCCESS;
+}
+
+
+static int handle_showmanagers(int fd, int argc, char *argv[])
+{
+ struct ast_manager_user *user = NULL;
+ int count_amu = 0;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ AST_LIST_LOCK(&users);
+
+ /* If there are no users, print out something along those lines */
+ if (AST_LIST_EMPTY(&users)) {
+ ast_cli(fd, "There are no manager users.\n");
+ AST_LIST_UNLOCK(&users);
+ return RESULT_SUCCESS;
+ }
+
+ ast_cli(fd, "\nusername\n--------\n");
+
+ AST_LIST_TRAVERSE(&users, user, list) {
+ ast_cli(fd, "%s\n", user->username);
+ count_amu++;
+ }
+
+ AST_LIST_UNLOCK(&users);
+
+ ast_cli(fd,"-------------------\n");
+ ast_cli(fd,"%d manager users configured.\n", count_amu);
+
+ return RESULT_SUCCESS;
+}
+
+
+/*! \brief CLI command
+ Should change to "manager show commands" */
+static int handle_showmancmds(int fd, int argc, char *argv[])
+{
+ struct manager_action *cur;
+ char authority[80];
+ char *format = " %-15.15s %-15.15s %-55.55s\n";
+
+ ast_cli(fd, format, "Action", "Privilege", "Synopsis");
+ ast_cli(fd, format, "------", "---------", "--------");
+
+ ast_rwlock_rdlock(&actionlock);
+ for (cur = first_action; cur; cur = cur->next) /* Walk the list of actions */
+ ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
+ ast_rwlock_unlock(&actionlock);
+
+ return RESULT_SUCCESS;
+}
+
+/*! \brief CLI command show manager connected */
+/* Should change to "manager show connected" */
+static int handle_showmanconn(int fd, int argc, char *argv[])
+{
+ struct mansession *s;
+ char *format = " %-15.15s %-15.15s\n";
+
+ ast_cli(fd, format, "Username", "IP Address");
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, s, list)
+ ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr));
+ AST_LIST_UNLOCK(&sessions);
+
+ return RESULT_SUCCESS;
+}
+
+/*! \brief CLI command show manager connected */
+/* Should change to "manager show connected" */
+static int handle_showmaneventq(int fd, int argc, char *argv[])
+{
+ struct eventqent *s;
+
+ AST_LIST_LOCK(&sessions);
+ for (s = master_eventq; s; s = s->next) {
+ ast_cli(fd, "Usecount: %d\n",s->usecount);
+ ast_cli(fd, "Category: %d\n", s->category);
+ ast_cli(fd, "Event:\n%s", s->eventdata);
+ }
+ AST_LIST_UNLOCK(&sessions);
+
+ return RESULT_SUCCESS;
+}
+
+static char showmancmd_help[] =
+"Usage: manager show command <actionname>\n"
+" Shows the detailed description for a specific Asterisk manager interface command.\n";
+
+static char showmancmds_help[] =
+"Usage: manager show commands\n"
+" Prints a listing of all the available Asterisk manager interface commands.\n";
+
+static char showmanconn_help[] =
+"Usage: manager show connected\n"
+" Prints a listing of the users that are currently connected to the\n"
+"Asterisk manager interface.\n";
+
+static char showmaneventq_help[] =
+"Usage: manager show eventq\n"
+" Prints a listing of all events pending in the Asterisk manger\n"
+"event queue.\n";
+
+static char showmanagers_help[] =
+"Usage: manager show users\n"
+" Prints a listing of all managers that are currently configured on that\n"
+" system.\n";
+
+static char showmanager_help[] =
+" Usage: manager show user <user>\n"
+" Display all information related to the manager user specified.\n";
+
+static struct ast_cli_entry cli_show_manager_command_deprecated = {
+ { "show", "manager", "command", NULL },
+ handle_showmancmd, NULL,
+ NULL, complete_show_mancmd };
+
+static struct ast_cli_entry cli_show_manager_commands_deprecated = {
+ { "show", "manager", "commands", NULL },
+ handle_showmancmds, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_manager_connected_deprecated = {
+ { "show", "manager", "connected", NULL },
+ handle_showmanconn, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_manager_eventq_deprecated = {
+ { "show", "manager", "eventq", NULL },
+ handle_showmaneventq, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_manager[] = {
+ { { "manager", "show", "command", NULL },
+ handle_showmancmd, "Show a manager interface command",
+ showmancmd_help, complete_show_mancmd, &cli_show_manager_command_deprecated },
+
+ { { "manager", "show", "commands", NULL },
+ handle_showmancmds, "List manager interface commands",
+ showmancmds_help, NULL, &cli_show_manager_commands_deprecated },
+
+ { { "manager", "show", "connected", NULL },
+ handle_showmanconn, "List connected manager interface users",
+ showmanconn_help, NULL, &cli_show_manager_connected_deprecated },
+
+ { { "manager", "show", "eventq", NULL },
+ handle_showmaneventq, "List manager interface queued events",
+ showmaneventq_help, NULL, &cli_show_manager_eventq_deprecated },
+
+ { { "manager", "show", "users", NULL },
+ handle_showmanagers, "List configured manager users",
+ showmanagers_help, NULL, NULL },
+
+ { { "manager", "show", "user", NULL },
+ handle_showmanager, "Display information on a specific manager user",
+ showmanager_help, NULL, NULL },
+};
+
+static void unuse_eventqent(struct eventqent *e)
+{
+ if (ast_atomic_dec_and_test(&e->usecount) && e->next)
+ pthread_kill(t, SIGURG);
+}
+
+static void free_session(struct mansession *s)
+{
+ struct eventqent *eqe;
+ if (s->fd > -1)
+ close(s->fd);
+ if (s->outputstr)
+ free(s->outputstr);
+ ast_mutex_destroy(&s->__lock);
+ while (s->eventq) {
+ eqe = s->eventq;
+ s->eventq = s->eventq->next;
+ unuse_eventqent(eqe);
+ }
+ free(s);
+}
+
+static void destroy_session(struct mansession *s)
+{
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_REMOVE(&sessions, s, list);
+ num_sessions--;
+ free_session(s);
+ AST_LIST_UNLOCK(&sessions);
+}
+
+const char *astman_get_header(const struct message *m, char *var)
+{
+ char cmp[80];
+ int x;
+
+ snprintf(cmp, sizeof(cmp), "%s: ", var);
+
+ for (x = 0; x < m->hdrcount; x++) {
+ if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
+ return m->headers[x] + strlen(cmp);
+ }
+
+ return "";
+}
+
+struct ast_variable *astman_get_variables(const struct message *m)
+{
+ int varlen, x, y;
+ struct ast_variable *head = NULL, *cur;
+ char *var, *val;
+
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(vars)[32];
+ );
+
+ varlen = strlen("Variable: ");
+
+ for (x = 0; x < m->hdrcount; x++) {
+ if (strncasecmp("Variable: ", m->headers[x], varlen))
+ continue;
+
+ parse = ast_strdupa(m->headers[x] + varlen);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+ if (args.argc) {
+ 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);
+ if (head) {
+ cur->next = head;
+ head = cur;
+ } else
+ head = cur;
+ }
+ }
+ }
+
+ return head;
+}
+
+/*! \note NOTE:
+ 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.
+ */
+void astman_send_error(struct mansession *s, const struct message *m, char *error)
+{
+ const char *id = astman_get_header(m,"ActionID");
+
+ astman_append(s, "Response: Error\r\n");
+ if (!ast_strlen_zero(id))
+ astman_append(s, "ActionID: %s\r\n", id);
+ astman_append(s, "Message: %s\r\n\r\n", error);
+}
+
+void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
+{
+ 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 (msg)
+ astman_append(s, "Message: %s\r\n\r\n", msg);
+ else
+ astman_append(s, "\r\n");
+}
+
+void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
+{
+ astman_send_response(s, m, "Success", msg);
+}
+
+/*! 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, 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;
+}
+
+static int ast_is_number(const char *string)
+{
+ int ret = 1, x = 0;
+
+ if (!string)
+ return 0;
+
+ for (x = 0; x < strlen(string); x++) {
+ if (!(string[x] >= 48 && string[x] <= 57)) {
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret ? atoi(string) : 0;
+}
+
+static int strings_to_mask(const char *string)
+{
+ int x, ret = -1;
+
+ x = ast_is_number(string);
+
+ if (x)
+ ret = x;
+ else if (ast_strlen_zero(string))
+ ret = -1;
+ else if (ast_false(string))
+ ret = 0;
+ else if (ast_true(string)) {
+ ret = 0;
+ for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
+ ret |= perms[x].num;
+ } else {
+ ret = 0;
+ for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
+ if (ast_instring(string, perms[x].label, ','))
+ ret |= perms[x].num;
+ }
+ }
+
+ return ret;
+}
+
+/*! \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;
+}
+
+static int authenticate(struct mansession *s, const struct message *m)
+{
+ struct ast_config *cfg;
+ char *cat;
+ const char *user = astman_get_header(m, "Username");
+ const char *pass = astman_get_header(m, "Secret");
+ const char *authtype = astman_get_header(m, "AuthType");
+ const char *key = astman_get_header(m, "Key");
+ const char *events = astman_get_header(m, "Events");
+
+ cfg = ast_config_load("manager.conf");
+ if (!cfg)
+ return -1;
+ cat = ast_category_browse(cfg, NULL);
+ while (cat) {
+ if (strcasecmp(cat, "general")) {
+ /* This is a user */
+ if (!strcasecmp(cat, user)) {
+ struct ast_variable *v;
+ struct ast_ha *ha = NULL;
+ char *password = NULL;
+
+ for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
+ if (!strcasecmp(v->name, "secret")) {
+ password = v->value;
+ } else if (!strcasecmp(v->name, "displaysystemname")) {
+ if (ast_true(v->value)) {
+ if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
+ s->displaysystemname = 1;
+ } else {
+ ast_log(LOG_ERROR, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
+ }
+ }
+ } else if (!strcasecmp(v->name, "permit") ||
+ !strcasecmp(v->name, "deny")) {
+ ha = ast_append_ha(v->name, v->value, ha);
+ } else if (!strcasecmp(v->name, "writetimeout")) {
+ int val = atoi(v->value);
+
+ if (val < 100)
+ ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
+ else
+ s->writetimeout = val;
+ }
+
+ }
+ if (ha && !ast_apply_ha(ha, &(s->sin))) {
+ ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
+ ast_free_ha(ha);
+ ast_config_destroy(cfg);
+ return -1;
+ } else if (ha)
+ ast_free_ha(ha);
+ if (!strcasecmp(authtype, "MD5")) {
+ if (!ast_strlen_zero(key) &&
+ !ast_strlen_zero(s->challenge) && !ast_strlen_zero(password)) {
+ 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 *) password, strlen(password));
+ MD5Final(digest, &md5);
+ for (x=0; x<16; x++)
+ len += sprintf(md5key + len, "%2.2x", digest[x]);
+ if (!strcmp(md5key, key))
+ break;
+ else {
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ } else {
+ ast_log(LOG_DEBUG, "MD5 authentication is not possible. challenge: '%s'\n",
+ S_OR(s->challenge, ""));
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ } else if (password && !strcmp(password, pass)) {
+ break;
+ } else {
+ ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ }
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ if (cat) {
+ ast_copy_string(s->username, cat, sizeof(s->username));
+ s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
+ s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
+ ast_config_destroy(cfg);
+ if (events)
+ set_eventmask(s, events);
+ return 0;
+ }
+ ast_config_destroy(cfg);
+ cfg = ast_config_load("users.conf");
+ if (!cfg)
+ return -1;
+ cat = ast_category_browse(cfg, NULL);
+ while (cat) {
+ struct ast_variable *v;
+ const char *password = NULL;
+ int hasmanager = 0;
+ const char *readperms = NULL;
+ const char *writeperms = NULL;
+
+ if (strcasecmp(cat, user) || !strcasecmp(cat, "general")) {
+ cat = ast_category_browse(cfg, cat);
+ continue;
+ }
+ for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
+ if (!strcasecmp(v->name, "secret"))
+ password = v->value;
+ else if (!strcasecmp(v->name, "hasmanager"))
+ hasmanager = ast_true(v->value);
+ else if (!strcasecmp(v->name, "managerread"))
+ readperms = v->value;
+ else if (!strcasecmp(v->name, "managerwrite"))
+ writeperms = v->value;
+ }
+ if (!hasmanager)
+ break;
+ if (!password || strcmp(password, pass)) {
+ ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ ast_copy_string(s->username, cat, sizeof(s->username));
+ s->readperm = readperms ? get_perm(readperms) : -1;
+ s->writeperm = writeperms ? get_perm(writeperms) : -1;
+ ast_config_destroy(cfg);
+ if (events)
+ set_eventmask(s, events);
+ return 0;
+ }
+ ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
+ ast_config_destroy(cfg);
+ return -1;
+}
+
+/*! \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, "Pong", NULL);
+ 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;
+ char idText[256] = "";
+ const char *id = astman_get_header(m, "ActionID");
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+
+ if (ast_strlen_zero(fn)) {
+ astman_send_error(s, m, "Filename not specified");
+ return 0;
+ }
+ if (!(cfg = ast_config_load_with_comments(fn))) {
+ astman_send_error(s, m, "Config file not found");
+ return 0;
+ }
+ astman_append(s, "Response: Success\r\n%s", idText);
+ 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;
+}
+
+
+static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg)
+{
+ 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);
+ 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, (char *) 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, (char *) var, (char *) 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))){
+ 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;
+ char idText[256] = "";
+ const char *id = astman_get_header(m, "ActionID");
+ const char *rld = astman_get_header(m, "Reload");
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+
+ if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
+ astman_send_error(s, m, "Filename not specified");
+ return 0;
+ }
+ if (!(cfg = ast_config_load_with_comments(sfn))) {
+ astman_send_error(s, m, "Config file not found");
+ return 0;
+ }
+ handle_updates(s, m, cfg);
+ 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_append(s, "Response: Success\r\n%s\r\n", idText);
+ 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 to wait for events\n";
+
+static int action_waitevent(struct mansession *s, const struct message *m)
+{
+ const char *timeouts = astman_get_header(m, "Timeout");
+ int timeout = -1, max;
+ int x;
+ int needexit = 0;
+ time_t now;
+ struct eventqent *eqe;
+ 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);
+ }
+
+ ast_mutex_lock(&s->__lock);
+ if (s->waiting_thread != AST_PTHREADT_NULL) {
+ pthread_kill(s->waiting_thread, SIGURG);
+ }
+ if (s->sessiontimeout) {
+ time(&now);
+ max = s->sessiontimeout - now - 10;
+ if (max < 0)
+ max = 0;
+ if ((timeout < 0) || (timeout > max))
+ timeout = max;
+ if (!s->send_events)
+ s->send_events = -1;
+ /* Once waitevent is called, always queue events from now on */
+ }
+ ast_mutex_unlock(&s->__lock);
+ s->waiting_thread = pthread_self();
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
+ for (x=0; ((x < timeout) || (timeout < 0)); x++) {
+ ast_mutex_lock(&s->__lock);
+ if (s->eventq && s->eventq->next)
+ needexit = 1;
+ if (s->waiting_thread != pthread_self())
+ needexit = 1;
+ if (s->needdestroy)
+ needexit = 1;
+ ast_mutex_unlock(&s->__lock);
+ if (needexit)
+ break;
+ if (s->fd > 0) {
+ if (ast_wait_for_input(s->fd, 1000))
+ break;
+ } else {
+ sleep(1);
+ }
+ }
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
+ ast_mutex_lock(&s->__lock);
+ if (s->waiting_thread == pthread_self()) {
+ astman_send_response(s, m, "Success", "Waiting for Event...");
+ /* Only show events if we're the most recent waiter */
+ while(s->eventq->next) {
+ eqe = s->eventq->next;
+ if (((s->readperm & eqe->category) == eqe->category) &&
+ ((s->send_events & eqe->category) == eqe->category)) {
+ astman_append(s, "%s", eqe->eventdata);
+ }
+ unuse_eventqent(s->eventq);
+ s->eventq = eqe;
+ }
+ astman_append(s,
+ "Event: WaitEventComplete\r\n"
+ "%s"
+ "\r\n", idText);
+ s->waiting_thread = AST_PTHREADT_NULL;
+ } else {
+ ast_log(LOG_DEBUG, "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;
+ char idText[256] = "";
+ char temp[BUFSIZ];
+ const char *id = astman_get_header(m,"ActionID");
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+ astman_append(s, "Response: Success\r\n%s", idText);
+ for (cur = first_action; cur; cur = cur->next) {
+ if ((s->writeperm & cur->authority) == cur->authority)
+ astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(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, "Events On", NULL);
+ else if (res == 0)
+ astman_send_response(s, m, "Events Off", NULL);
+
+ 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 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");
+ const char *id = astman_get_header(m,"ActionID");
+ 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] == ')') {
+ char *copy = ast_strdupa(varname);
+ if (!c) {
+ c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/manager");
+ if (c) {
+ ast_func_read(c, copy, workspace, sizeof(workspace));
+ ast_channel_free(c);
+ } else
+ ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
+ } else
+ ast_func_read(c, copy, workspace, sizeof(workspace));
+ varval = workspace;
+ } else {
+ pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
+ }
+
+ if (c)
+ ast_channel_unlock(c);
+ astman_append(s, "Response: Success\r\n"
+ "Variable: %s\r\nValue: %s\r\n", varname, varval);
+ if (!ast_strlen_zero(id))
+ astman_append(s, "ActionID: %s\r\n",id);
+ astman_append(s, "\r\n");
+
+ return 0;
+}
+
+
+/*! \brief Manager "status" command to show channels */
+/* Needs documentation... */
+static int action_status(struct mansession *s, const struct message *m)
+{
+ const char *id = astman_get_header(m,"ActionID");
+ const char *name = astman_get_header(m,"Channel");
+ char idText[256] = "";
+ struct ast_channel *c;
+ char bridge[256];
+ struct timeval now = ast_tvnow();
+ long elapsed_seconds = 0;
+ int all = ast_strlen_zero(name); /* set if we want all channels */
+
+ 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) {
+ if (c->_bridge)
+ snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
+ 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"
+ "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Account: %s\r\n"
+ "State: %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, "<unknown>"),
+ S_OR(c->cid.cid_num, "<unknown>"),
+ S_OR(c->cid.cid_name, "<unknown>"),
+ c->accountcode,
+ 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"
+ "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
+ "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_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"
+ "\r\n",idText);
+ return 0;
+}
+
+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");
+ return 0;
+ }
+ }
+ /* XXX watch out, possible deadlock!!! */
+ 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.");
+ 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.");
+ 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 int check_blacklist(const char *cmd)
+{
+ char *cmd_copy, *cur_cmd;
+ char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
+ int i;
+
+ cmd_copy = ast_strdupa(cmd);
+ for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
+ cur_cmd = ast_strip(cur_cmd);
+ if (ast_strlen_zero(cur_cmd)) {
+ i--;
+ continue;
+ }
+
+ cmd_words[i] = cur_cmd;
+ }
+
+ for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
+ int j, match = 1;
+
+ for (j = 0; command_blacklist[i].words[j]; j++) {
+ if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
+ match = 0;
+ break;
+ }
+ }
+
+ if (match) {
+ return 1;
+ }
+ }
+
+ 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 action_command: 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);
+ off_t l;
+
+ if (ast_strlen_zero(cmd)) {
+ astman_send_error(s, m, "No command provided");
+ return 0;
+ }
+
+ if (check_blacklist(cmd)) {
+ 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);
+ if (read(fd, buf, l) < 0) {
+ ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
+ }
+ buf[l] = '\0';
+ if (final_buf) {
+ term_strip(final_buf, buf, l);
+ final_buf[l] = '\0';
+ }
+ astman_append(s, "%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;
+}
+
+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, in->format, 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, in->format, 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"
+ "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
+ "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_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);
+ 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");
+ const char *codecs = astman_get_header(m, "Codecs");
+ 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];
+ int format = AST_FORMAT_SLINEAR;
+
+ pthread_t th;
+ pthread_attr_t attr;
+ 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");
+ return 0;
+ }
+ }
+ if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
+ astman_send_error(s, m, "Invalid timeout");
+ return 0;
+ }
+ ast_copy_string(tmp, name, sizeof(tmp));
+ tech = tmp;
+ data = strchr(tmp, '/');
+ if (!data) {
+ astman_send_error(s, m, "Invalid channel");
+ 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_strlen_zero(codecs)) {
+ format = 0;
+ ast_parse_allow_disallow(NULL, &format, codecs, 1);
+ }
+ 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->format = format;
+ fast->timeout = to;
+ fast->priority = pi;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
+ ast_free(fast);
+ res = -1;
+ } else {
+ res = 0;
+ }
+ pthread_attr_destroy(&attr);
+ }
+ } else if (!ast_strlen_zero(app)) {
+ res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
+ } else {
+ if (exten && context && pi)
+ res = ast_pbx_outgoing_exten(tech, format, 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");
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[256] = "";
+ int ret;
+ if (ast_strlen_zero(mailbox)) {
+ astman_send_error(s, m, "Mailbox not specified");
+ return 0;
+ }
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+ ret = ast_app_has_voicemail(mailbox, NULL);
+ astman_append(s, "Response: Success\r\n"
+ "%s"
+ "Message: Mailbox Status\r\n"
+ "Mailbox: %s\r\n"
+ "Waiting: %d\r\n\r\n", idText, 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");
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[256] = "";
+ 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);
+ if (!ast_strlen_zero(id)) {
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
+ }
+ astman_append(s, "Response: Success\r\n"
+ "%s"
+ "Message: Mailbox Message Count\r\n"
+ "Mailbox: %s\r\n"
+ "NewMessages: %d\r\n"
+ "OldMessages: %d\r\n"
+ "\r\n",
+ idText,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");
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[256] = "";
+ 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);
+ if (!ast_strlen_zero(id)) {
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+ }
+ astman_append(s, "Response: Success\r\n"
+ "%s"
+ "Message: Extension Status\r\n"
+ "Exten: %s\r\n"
+ "Context: %s\r\n"
+ "Hint: %s\r\n"
+ "Status: %d\r\n\r\n",
+ idText,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 = NULL;
+ 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;
+}
+
+static int process_events(struct mansession *s)
+{
+ struct eventqent *eqe;
+ int ret = 0;
+ ast_mutex_lock(&s->__lock);
+ if (!s->eventq)
+ s->eventq = master_eventq;
+ while(s->eventq->next) {
+ eqe = s->eventq->next;
+ if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
+ ((s->send_events & eqe->category) == eqe->category)) {
+ if (s->fd > -1) {
+ if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
+ ret = -1;
+ } else if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr))))
+ ret = -1;
+ else
+ ast_dynamic_str_append(&s->outputstr, 0, "%s", eqe->eventdata);
+ }
+ unuse_eventqent(s->eventq);
+ s->eventq = eqe;
+ }
+ 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 int process_message(struct mansession *s, const struct message *m)
+{
+ char action[80] = "";
+ struct manager_action *tmp;
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[256] = "";
+ int ret = 0;
+
+ ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
+ if (option_debug)
+ ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
+
+ if (ast_strlen_zero(action)) {
+ astman_send_error(s, m, "Missing action in request");
+ return 0;
+ }
+ if (!ast_strlen_zero(id)) {
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+ }
+ if (!s->authenticated) {
+ if (!strcasecmp(action, "Challenge")) {
+ 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());
+ astman_append(s, "Response: Success\r\n"
+ "%s"
+ "Challenge: %s\r\n\r\n",
+ idText, s->challenge);
+ return 0;
+ } else {
+ astman_send_error(s, m, "Must specify AuthType");
+ return 0;
+ }
+ } else if (!strcasecmp(action, "Login")) {
+ if (authenticate(s, m)) {
+ sleep(1);
+ astman_send_error(s, m, "Authentication failed");
+ return -1;
+ } else {
+ s->authenticated = 1;
+ if (option_verbose > 1) {
+ if (displayconnects) {
+ ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n",
+ (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
+ }
+ }
+ ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n",
+ (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
+ astman_send_ack(s, m, "Authentication accepted");
+ }
+ } else if (!strcasecmp(action, "Logoff")) {
+ astman_send_ack(s, m, "See ya");
+ return -1;
+ } else
+ astman_send_error(s, m, "Authentication Required");
+ } else {
+ if (!strcasecmp(action, "Login"))
+ astman_send_ack(s, m, "Already logged in");
+ else {
+ ast_rwlock_rdlock(&actionlock);
+ for (tmp = first_action; tmp; tmp = tmp->next) {
+ if (strcasecmp(action, tmp->action))
+ continue;
+ if ((s->writeperm & tmp->authority) == tmp->authority) {
+ if (tmp->func(s, m))
+ ret = -1;
+ } else
+ astman_send_error(s, m, "Permission denied");
+ break;
+ }
+ ast_rwlock_unlock(&actionlock);
+ if (!tmp)
+ astman_send_error(s, m, "Invalid/unknown command");
+ }
+ }
+ if (ret)
+ return ret;
+ return process_events(s);
+}
+
+static int get_input(struct mansession *s, char *output)
+{
+ /* output must have at least sizeof(s->inbuf) space */
+ int res;
+ int x;
+ struct pollfd fds[1];
+ for (x = 1; x < s->inlen; x++) {
+ if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
+ /* Copy output data up to and including \r\n */
+ memcpy(output, s->inbuf, x + 1);
+ /* Add trailing \0 */
+ output[x+1] = '\0';
+ /* Move remaining data back to the front */
+ memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
+ s->inlen -= (x + 1);
+ return 1;
+ }
+ }
+ if (s->inlen >= sizeof(s->inbuf) - 1) {
+ ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
+ s->inlen = 0;
+ }
+ fds[0].fd = s->fd;
+ fds[0].events = POLLIN;
+ do {
+ ast_mutex_lock(&s->__lock);
+ if (s->pending_event) {
+ s->pending_event = 0;
+ ast_mutex_unlock(&s->__lock);
+ return 0;
+ }
+ s->waiting_thread = pthread_self();
+ ast_mutex_unlock(&s->__lock);
+
+ res = poll(fds, 1, -1);
+
+ ast_mutex_lock(&s->__lock);
+ s->waiting_thread = AST_PTHREADT_NULL;
+ ast_mutex_unlock(&s->__lock);
+ if (res < 0) {
+ if (errno == EINTR || errno == EAGAIN) {
+ return 0;
+ }
+ ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
+ return -1;
+ } else if (res > 0) {
+ ast_mutex_lock(&s->__lock);
+ res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
+ ast_mutex_unlock(&s->__lock);
+ if (res < 1)
+ return -1;
+ break;
+ }
+ } while(1);
+ s->inlen += res;
+ s->inbuf[s->inlen] = '\0';
+ return 0;
+}
+
+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 (s->eventq->next) {
+ if (process_events(s))
+ return -1;
+ }
+ res = get_input(s, header_buf);
+ if (res == 0) {
+ continue;
+ } else if (res > 0) {
+ /* Strip trailing \r\n */
+ if (strlen(header_buf) < 2)
+ continue;
+ header_buf[strlen(header_buf) - 2] = '\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;
+ }
+ }
+}
+
+static void *session_do(void *data)
+{
+ struct mansession *s = data;
+ int res;
+
+ astman_append(s, "Asterisk Call Manager/1.0\r\n");
+ for (;;) {
+ if ((res = do_message(s)) < 0)
+ break;
+ }
+ if (s->authenticated) {
+ if (option_verbose > 1) {
+ if (displayconnects)
+ ast_verbose(VERBOSE_PREFIX_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 (option_verbose > 1) {
+ if (displayconnects)
+ ast_verbose(VERBOSE_PREFIX_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);
+ return NULL;
+}
+
+static void *accept_thread(void *ignore)
+{
+ int as;
+ struct sockaddr_in sin;
+ socklen_t sinlen;
+ struct eventqent *eqe;
+ struct mansession *s;
+ struct protoent *p;
+ int arg = 1;
+ int flags;
+ pthread_attr_t attr;
+ time_t now;
+ struct pollfd pfds[1];
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ for (;;) {
+ time(&now);
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
+ if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
+ AST_LIST_REMOVE_CURRENT(&sessions, list);
+ num_sessions--;
+ if (s->authenticated && (option_verbose > 1) && displayconnects) {
+ ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
+ s->username, ast_inet_ntoa(s->sin.sin_addr));
+ }
+ free_session(s);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ /* Purge master event queue of old, unused events, but make sure we
+ always keep at least one in the queue */
+ eqe = master_eventq;
+ while (master_eventq->next && !master_eventq->usecount) {
+ eqe = master_eventq;
+ master_eventq = master_eventq->next;
+ free(eqe);
+ }
+ AST_LIST_UNLOCK(&sessions);
+
+ sinlen = sizeof(sin);
+ pfds[0].fd = asock;
+ pfds[0].events = POLLIN;
+ /* Wait for something to happen, but timeout every few seconds so
+ we can ditch any old manager sessions */
+ if (poll(pfds, 1, 5000) < 1)
+ continue;
+ as = accept(asock, (struct sockaddr *)&sin, &sinlen);
+ if (as < 0) {
+ ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
+ continue;
+ }
+ p = getprotobyname("tcp");
+ if (p) {
+ if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
+ ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
+ }
+ }
+ if (!(s = ast_calloc(1, sizeof(*s))))
+ continue;
+
+ memcpy(&s->sin, &sin, sizeof(sin));
+ s->writetimeout = 100;
+ s->waiting_thread = AST_PTHREADT_NULL;
+
+ if (!block_sockets) {
+ /* For safety, make sure socket is non-blocking */
+ flags = fcntl(as, F_GETFL);
+ fcntl(as, F_SETFL, flags | O_NONBLOCK);
+ } else {
+ flags = fcntl(as, F_GETFL);
+ fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
+ }
+ ast_mutex_init(&s->__lock);
+ s->fd = as;
+ s->send_events = -1;
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_INSERT_HEAD(&sessions, s, list);
+ num_sessions++;
+ /* Find the last place in the master event queue and hook ourselves
+ in there */
+ s->eventq = master_eventq;
+ while(s->eventq->next)
+ s->eventq = s->eventq->next;
+ ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
+ AST_LIST_UNLOCK(&sessions);
+ if (ast_pthread_create_background(&s->t, &attr, session_do, s))
+ destroy_session(s);
+ }
+ pthread_attr_destroy(&attr);
+ return NULL;
+}
+
+static int append_event(const char *str, int category)
+{
+ struct eventqent *tmp, *prev = NULL;
+ tmp = ast_malloc(sizeof(*tmp) + strlen(str));
+
+ if (!tmp)
+ return -1;
+
+ tmp->next = NULL;
+ tmp->category = category;
+ strcpy(tmp->eventdata, str);
+
+ if (master_eventq) {
+ prev = master_eventq;
+ while (prev->next)
+ prev = prev->next;
+ prev->next = tmp;
+ } else {
+ master_eventq = tmp;
+ }
+
+ tmp->usecount = num_sessions;
+
+ return 0;
+}
+
+/*! \brief manager_event: Send AMI event to client */
+int manager_event(int category, const char *event, const char *fmt, ...)
+{
+ struct mansession *s;
+ char auth[80];
+ va_list ap;
+ struct timeval now;
+ struct ast_dynamic_str *buf;
+
+ /* Abort if there aren't any manager sessions */
+ if (!num_sessions)
+ return 0;
+
+ if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
+ return -1;
+
+ ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
+ "Event: %s\r\nPrivilege: %s\r\n",
+ event, authority_to_str(category, auth, sizeof(auth)));
+
+ if (timestampevents) {
+ now = ast_tvnow();
+ ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
+ "Timestamp: %ld.%06lu\r\n",
+ now.tv_sec, (unsigned long) now.tv_usec);
+ }
+
+ va_start(ap, fmt);
+ ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
+ va_end(ap);
+
+ ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");
+
+ /* Append event to master list and wake up any sleeping sessions */
+ AST_LIST_LOCK(&sessions);
+ append_event(buf->str, category);
+ AST_LIST_TRAVERSE(&sessions, s, list) {
+ ast_mutex_lock(&s->__lock);
+ if (s->waiting_thread != AST_PTHREADT_NULL)
+ pthread_kill(s->waiting_thread, SIGURG);
+ else
+ /* We have an event to process, but the mansession is
+ * not waiting for it. We still need to indicate that there
+ * is an event waiting so that get_input processes the pending
+ * event instead of polling.
+ */
+ s->pending_event = 1;
+ ast_mutex_unlock(&s->__lock);
+ }
+ AST_LIST_UNLOCK(&sessions);
+
+ return 0;
+}
+
+int ast_manager_unregister(char *action)
+{
+ struct manager_action *cur, *prev;
+
+ ast_rwlock_wrlock(&actionlock);
+ cur = prev = first_action;
+ while (cur) {
+ if (!strcasecmp(action, cur->action)) {
+ prev->next = cur->next;
+ free(cur);
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
+ ast_rwlock_unlock(&actionlock);
+ return 0;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ ast_rwlock_unlock(&actionlock);
+ return 0;
+}
+
+static int manager_state_cb(char *context, char *exten, int state, void *data)
+{
+ /* Notify managers of change */
+ manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
+ return 0;
+}
+
+static int ast_manager_register_struct(struct manager_action *act)
+{
+ struct manager_action *cur, *prev = NULL;
+ int ret;
+
+ ast_rwlock_wrlock(&actionlock);
+ cur = first_action;
+ while (cur) { /* Walk the list of actions */
+ ret = strcasecmp(cur->action, act->action);
+ if (ret == 0) {
+ ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
+ ast_rwlock_unlock(&actionlock);
+ return -1;
+ } else if (ret > 0) {
+ /* Insert these alphabetically */
+ if (prev) {
+ act->next = prev->next;
+ prev->next = act;
+ } else {
+ act->next = first_action;
+ first_action = act;
+ }
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if (!cur) {
+ if (prev)
+ prev->next = act;
+ else
+ first_action = act;
+ act->next = NULL;
+ }
+
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
+ ast_rwlock_unlock(&actionlock);
+ 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;
+
+ cur = ast_malloc(sizeof(*cur));
+ if (!cur)
+ return -1;
+
+ cur->action = action;
+ cur->authority = auth;
+ cur->func = func;
+ cur->synopsis = synopsis;
+ cur->description = description;
+ cur->next = NULL;
+
+ ast_manager_register_struct(cur);
+
+ return 0;
+}
+/*! @}
+ END Doxygen group */
+
+static struct mansession *find_session(uint32_t ident)
+{
+ struct mansession *s;
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, s, list) {
+ ast_mutex_lock(&s->__lock);
+ if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
+ s->inuse++;
+ break;
+ }
+ ast_mutex_unlock(&s->__lock);
+ }
+ AST_LIST_UNLOCK(&sessions);
+
+ return s;
+}
+
+int astman_verify_session_readpermissions(uint32_t 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(uint32_t 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;
+}
+
+enum {
+ FORMAT_RAW,
+ FORMAT_HTML,
+ FORMAT_XML,
+};
+static char *contenttype[] = { "plain", "html", "xml" };
+
+static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+{
+ struct mansession *s = NULL;
+ uint32_t ident = 0;
+ char workspace[512];
+ char cookie[128];
+ size_t len = sizeof(workspace);
+ int blastaway = 0;
+ char *c = workspace;
+ char *retval = NULL;
+ struct ast_variable *v;
+
+ for (v = params; v; v = v->next) {
+ if (!strcasecmp(v->name, "mansession_id")) {
+ sscanf(v->value, "%x", &ident);
+ break;
+ }
+ }
+
+ if (!(s = find_session(ident))) {
+ /* Create new session */
+ if (!(s = ast_calloc(1, sizeof(*s)))) {
+ *status = 500;
+ goto generic_callback_out;
+ }
+ memcpy(&s->sin, requestor, sizeof(s->sin));
+ 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;
+ /*!\note There is approximately a 1 in 1.8E19 chance that the following
+ * calculation will produce 0, which is an invalid ID, but due to the
+ * properties of the rand() function (and the constantcy of s), that
+ * won't happen twice in a row.
+ */
+ while ((s->managerid = rand() ^ (unsigned long) s) == 0);
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_INSERT_HEAD(&sessions, s, list);
+ /* Hook into the last spot in the event queue */
+ s->eventq = master_eventq;
+ while (s->eventq->next)
+ s->eventq = s->eventq->next;
+ ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
+ ast_atomic_fetchadd_int(&num_sessions, 1);
+ AST_LIST_UNLOCK(&sessions);
+ }
+
+ /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
+ time(&s->sessiontimeout);
+ if (!s->authenticated && (httptimeout > 5))
+ s->sessiontimeout += 5;
+ else
+ s->sessiontimeout += httptimeout;
+ ast_mutex_unlock(&s->__lock);
+
+ if (s) {
+ struct message m = { 0 };
+ char tmp[80];
+ unsigned int x;
+ size_t hdrlen;
+
+ 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 (option_verbose > 1) {
+ if (displayconnects)
+ ast_verbose(VERBOSE_PREFIX_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 (option_verbose > 1) {
+ if (displayconnects)
+ ast_verbose(VERBOSE_PREFIX_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_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
+ sprintf(tmp, "%08x", s->managerid);
+ ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
+ if (format == FORMAT_HTML)
+ ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Interface</title>");
+ if (format == FORMAT_XML) {
+ ast_build_string(&c, &len, "<ajax-response>\n");
+ } else if (format == FORMAT_HTML) {
+ ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
+ ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
+ }
+ ast_mutex_lock(&s->__lock);
+ if (s->outputstr) {
+ char *tmp;
+ if (format == FORMAT_XML)
+ tmp = xml_translate(s->outputstr->str, params);
+ else if (format == FORMAT_HTML)
+ tmp = html_translate(s->outputstr->str);
+ else
+ tmp = s->outputstr->str;
+ if (tmp) {
+ retval = malloc(strlen(workspace) + strlen(tmp) + 128);
+ if (retval) {
+ strcpy(retval, workspace);
+ strcpy(retval + strlen(retval), tmp);
+ c = retval + strlen(retval);
+ len = 120;
+ }
+ }
+ if (tmp != s->outputstr->str)
+ free(tmp);
+ free(s->outputstr);
+ s->outputstr = NULL;
+ }
+ ast_mutex_unlock(&s->__lock);
+ /* Still okay because c would safely be pointing to workspace even
+ if retval failed to allocate above */
+ if (format == FORMAT_XML) {
+ ast_build_string(&c, &len, "</ajax-response>\n");
+ } else if (format == FORMAT_HTML)
+ ast_build_string(&c, &len, "</table></body>\r\n");
+ } else {
+ *status = 500;
+ *title = strdup("Server Error");
+ }
+ ast_mutex_lock(&s->__lock);
+ if (s->needdestroy) {
+ if (s->inuse == 1) {
+ ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
+ blastaway = 1;
+ } else {
+ ast_log(LOG_DEBUG, "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 retval;
+}
+
+static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+{
+ return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
+}
+
+static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+{
+ return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
+}
+
+static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+{
+ return generic_http_callback(FORMAT_RAW, 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;
+
+int init_manager(void)
+{
+ struct ast_config *cfg = NULL, *ucfg = NULL;
+ const char *val;
+ char *cat = NULL;
+ int oldportno = portno;
+ static struct sockaddr_in ba;
+ int x = 1;
+ int flags;
+ int webenabled = 0;
+ int newhttptimeout = 60;
+ struct ast_manager_user *user = NULL;
+
+ 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("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
+ ast_manager_register("Status", EVENT_FLAG_CALL, 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, action_getvar, "Gets a Channel Variable", mandescr_getvar );
+ ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
+ 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, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
+ ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
+ ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
+ ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
+ ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
+ 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_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);
+ }
+ portno = DEFAULT_MANAGER_PORT;
+ displayconnects = 1;
+ cfg = ast_config_load("manager.conf");
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf. Call management disabled.\n");
+ return 0;
+ }
+ val = ast_variable_retrieve(cfg, "general", "enabled");
+ if (val)
+ enabled = ast_true(val);
+
+ val = ast_variable_retrieve(cfg, "general", "block-sockets");
+ if (val)
+ block_sockets = ast_true(val);
+
+ val = ast_variable_retrieve(cfg, "general", "webenabled");
+ if (val)
+ webenabled = ast_true(val);
+
+ if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
+ if (sscanf(val, "%d", &portno) != 1) {
+ ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
+ portno = DEFAULT_MANAGER_PORT;
+ }
+ }
+
+ if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
+ displayconnects = ast_true(val);
+
+ if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
+ timestampevents = ast_true(val);
+
+ if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
+ newhttptimeout = atoi(val);
+
+ memset(&ba, 0, sizeof(ba));
+ ba.sin_family = AF_INET;
+ ba.sin_port = htons(portno);
+
+ if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
+ if (!inet_aton(val, &ba.sin_addr)) {
+ ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
+ memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
+ }
+ }
+
+
+ if ((asock > -1) && ((portno != oldportno) || !enabled)) {
+#if 0
+ /* Can't be done yet */
+ close(asock);
+ asock = -1;
+#else
+ ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
+#endif
+ }
+
+ AST_LIST_LOCK(&users);
+
+ if ((ucfg = ast_config_load("users.conf"))) {
+ while ((cat = ast_category_browse(ucfg, cat))) {
+ int hasmanager = 0;
+ struct ast_variable *var = NULL;
+
+ if (!strcasecmp(cat, "general")) {
+ continue;
+ }
+
+ if (!(hasmanager = ast_true(ast_variable_retrieve(ucfg, cat, "hasmanager")))) {
+ continue;
+ }
+
+ /* Look for an existing entry, if none found - create one and add it to the list */
+ if (!(user = ast_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);
+ }
+
+ /* Make sure we keep this user and don't destroy it during cleanup */
+ user->keep = 1;
+
+ for (var = ast_variable_browse(ucfg, cat); var; var = var->next) {
+ if (!strcasecmp(var->name, "secret")) {
+ if (user->secret) {
+ free(user->secret);
+ }
+ user->secret = ast_strdup(var->value);
+ } else if (!strcasecmp(var->name, "deny") ) {
+ if (user->deny) {
+ free(user->deny);
+ }
+ user->deny = ast_strdup(var->value);
+ } else if (!strcasecmp(var->name, "permit") ) {
+ if (user->permit) {
+ free(user->permit);
+ }
+ user->permit = ast_strdup(var->value);
+ } else if (!strcasecmp(var->name, "read") ) {
+ if (user->read) {
+ free(user->read);
+ }
+ user->read = ast_strdup(var->value);
+ } else if (!strcasecmp(var->name, "write") ) {
+ if (user->write) {
+ free(user->write);
+ }
+ user->write = ast_strdup(var->value);
+ } else if (!strcasecmp(var->name, "displayconnects") ) {
+ user->displayconnects = ast_true(var->value);
+ } else if (!strcasecmp(var->name, "hasmanager")) {
+ /* already handled */
+ } else {
+ ast_log(LOG_DEBUG, "%s is an unknown option (to the manager module).\n", var->name);
+ }
+ }
+ }
+ ast_config_destroy(ucfg);
+ }
+
+ while ((cat = ast_category_browse(cfg, cat))) {
+ struct ast_variable *var = NULL;
+
+ if (!strcasecmp(cat, "general"))
+ continue;
+
+ /* Look for an existing entry, if none found - create one and add it to the list */
+ if (!(user = ast_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);
+ }
+
+ /* Make sure we keep this user and don't destroy it during cleanup */
+ user->keep = 1;
+
+ var = ast_variable_browse(cfg, cat);
+ while (var) {
+ if (!strcasecmp(var->name, "secret")) {
+ if (user->secret)
+ free(user->secret);
+ user->secret = ast_strdup(var->value);
+ } else if (!strcasecmp(var->name, "deny") ) {
+ if (user->deny)
+ free(user->deny);
+ user->deny = ast_strdup(var->value);
+ } else if (!strcasecmp(var->name, "permit") ) {
+ if (user->permit)
+ free(user->permit);
+ user->permit = ast_strdup(var->value);
+ } else if (!strcasecmp(var->name, "read") ) {
+ if (user->read)
+ free(user->read);
+ user->read = ast_strdup(var->value);
+ } else if (!strcasecmp(var->name, "write") ) {
+ if (user->write)
+ free(user->write);
+ user->write = ast_strdup(var->value);
+ } else if (!strcasecmp(var->name, "displayconnects") )
+ user->displayconnects = ast_true(var->value);
+ else
+ ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
+ var = var->next;
+ }
+ }
+
+ /* Perform cleanup - essentially prune out old users that no longer exist */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
+ if (user->keep) {
+ user->keep = 0;
+ continue;
+ }
+ /* We do not need to keep this user so take them out of the list */
+ AST_LIST_REMOVE_CURRENT(&users, list);
+ /* Free their memory now */
+ if (user->secret)
+ free(user->secret);
+ if (user->deny)
+ free(user->deny);
+ if (user->permit)
+ free(user->permit);
+ if (user->read)
+ free(user->read);
+ if (user->write)
+ free(user->write);
+ free(user);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ AST_LIST_UNLOCK(&users);
+
+ ast_config_destroy(cfg);
+
+ if (webenabled && 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;
+
+ /* If not enabled, do nothing */
+ if (!enabled)
+ return 0;
+
+ if (asock < 0) {
+ asock = socket(AF_INET, SOCK_STREAM, 0);
+ if (asock < 0) {
+ ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
+ return -1;
+ }
+ setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
+ if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
+ ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
+ close(asock);
+ asock = -1;
+ return -1;
+ }
+ if (listen(asock, 2)) {
+ ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
+ close(asock);
+ asock = -1;
+ return -1;
+ }
+ flags = fcntl(asock, F_GETFL);
+ fcntl(asock, F_SETFL, flags | O_NONBLOCK);
+ if (option_verbose)
+ ast_verbose("Asterisk Management interface listening on port %d\n", portno);
+ ast_pthread_create_background(&t, NULL, accept_thread, NULL);
+ }
+ return 0;
+}
+
+int reload_manager(void)
+{
+ manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
+ return init_manager();
+}
diff --git a/main/md5.c b/main/md5.c
new file mode 100644
index 000000000..949c408e2
--- /dev/null
+++ b/main/md5.c
@@ -0,0 +1,267 @@
+
+/*!\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 <string.h> /* for memcpy() */
+
+#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/main/netsock.c b/main/netsock.c
new file mode 100644
index 000000000..9fa842ec3
--- /dev/null
+++ b/main/netsock.c
@@ -0,0 +1,217 @@
+/*
+ * 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$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/ioctl.h>
+
+#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
+#include <fcntl.h>
+#include <net/route.h>
+#endif
+
+#if defined (SOLARIS)
+#include <sys/sockio.h>
+#endif
+
+#include "asterisk/netsock.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/srv.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);
+ 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, 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;
+ }
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
+
+ if (setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)))
+ ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
+
+ ast_enable_packet_fragmentation(netsocket);
+
+ if (!(ns = ast_calloc(1, sizeof(struct ast_netsock)))) {
+ 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);
+ 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;
+}
+
+struct ast_netsock *ast_netsock_bind(struct ast_netsock_list *list, struct io_context *ioc, const char *bindinfo, int defaultport, int tos, 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, 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/main/pbx.c b/main/pbx.c
new file mode 100644
index 000000000..a1a815046
--- /dev/null
+++ b/main/pbx.c
@@ -0,0 +1,6401 @@
+/*
+ * 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 <sys/types.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <limits.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/cli.h"
+#include "asterisk/pbx.h"
+#include "asterisk/channel.h"
+#include "asterisk/options.h"
+#include "asterisk/logger.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/threadstorage.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 ;-)
+ *
+ */
+
+#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)
+
+AST_APP_OPTIONS(waitexten_opts, {
+ AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0),
+});
+
+struct ast_context;
+
+AST_THREADSTORAGE(switch_data, switch_data_init);
+
+/*!
+ \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 */
+ void *data; /*!< Data to use (arguments) */
+ void (*datad)(void *); /*!< Data destructor */
+ struct ast_exten *peer; /*!< Next higher priority with our extension */
+ 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 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 ast_context: An extension context */
+struct ast_context {
+ ast_mutex_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_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_LIST_ENTRY(ast_app) list; /*!< Next app in list */
+ struct 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) */
+struct ast_hint {
+ struct ast_exten *exten; /*!< Extension */
+ int laststate; /*!< Last known state */
+ struct ast_state_cb *callbacks; /*!< Callback list for this extension */
+ AST_LIST_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" }
+};
+
+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_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_setglobalvar(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 *);
+int pbx_builtin_setvar(struct ast_channel *, void *);
+static int pbx_builtin_importvar(struct ast_channel *, void *);
+
+AST_MUTEX_DEFINE_STATIC(globalslock);
+static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+
+static int autofallthrough = 1;
+
+AST_MUTEX_DEFINE_STATIC(maxcalllock);
+static int countcalls;
+
+static AST_LIST_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"
+ "See Also: Playback (application) -- Play sound file(s) to the channel,\n"
+ " that cannot be interrupted\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"
+ },
+
+ { "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"
+ },
+
+ { "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"
+ },
+
+ { "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",
+ " NoOp(): This applicatiion 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."
+ },
+
+ { "Progress", pbx_builtin_progress,
+ "Indicate progress",
+ " Progress(): This application will request that in-band progress information\n"
+ "be provided to the calling channel.\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"
+ },
+
+ { "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"
+ },
+
+ { "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"
+ },
+
+ { "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"
+ },
+
+ { "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"
+ },
+
+ { "SetAMAFlags", pbx_builtin_setamaflags,
+ "Set the AMA Flags",
+ " SetAMAFlags([flag]): This application will set the channel's AMA Flags for\n"
+ " billing purposes.\n"
+ },
+
+ { "SetGlobalVar", pbx_builtin_setglobalvar,
+ "Set a global variable to a given value",
+ " SetGlobalVar(variable=value): This application sets a given global variable to\n"
+ "the specified value.\n"
+ "\n\nThis application is deprecated in favor of Set(GLOBAL(var)=value)\n"
+ },
+
+ { "Set", pbx_builtin_setvar,
+ "Set channel variable(s) or function value(s)",
+ " Set(name1=value1|name2=value2|..[|options])\n"
+ "This function can be used to set the value of channel variables or dialplan\n"
+ "functions. It will accept up to 24 name/value pairs. When setting variables,\n"
+ "if the variable name is prefixed with _, the variable will be inherited into\n"
+ "channels created from the current channel. If the variable name is prefixed\n"
+ "with __, the variable will be inherited into channels created from the current\n"
+ "channel and all children channels.\n"
+ " Options:\n"
+ " g - Set variable globally instead of on the channel\n"
+ " (applies only to variables, not functions)\n"
+ "\n\nThe use of Set to set multiple variables at once and the g flag have both\n"
+ "been deprecated. Please use multiple Set calls and the GLOBAL() dialplan\n"
+ "function instead.\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"
+ },
+
+ { "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"
+ "See Also: Playback(application), Background(application).\n"
+ },
+
+};
+
+static struct ast_context *contexts;
+AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
+
+static AST_LIST_HEAD_STATIC(apps, ast_app);
+
+static AST_LIST_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_LIST_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;
+
+ 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;
+ /* XXX remember what to to when we have linked apps to modules */
+ if (app->module) {
+ /* XXX LOCAL_USER_ADD(app->module) */
+ }
+ res = app->execute(c, S_OR(data, ""));
+ if (app->module) {
+ /* XXX LOCAL_USER_REMOVE(app->module) */
+ }
+ /* 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_LIST_LOCK(&apps);
+ AST_LIST_TRAVERSE(&apps, tmp, list) {
+ if (!strcasecmp(tmp->name, app))
+ break;
+ }
+ AST_LIST_UNLOCK(&apps);
+
+ return tmp;
+}
+
+static struct ast_switch *pbx_findswitch(const char *sw)
+{
+ struct ast_switch *asw;
+
+ AST_LIST_LOCK(&switches);
+ AST_LIST_TRAVERSE(&switches, asw, list) {
+ if (!strcasecmp(asw->name, sw))
+ break;
+ }
+ AST_LIST_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)
+{
+ free(p);
+}
+
+/*
+ * 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. Why? so
+ * patterns like NXX-XXX-XXXX or NXX XXX XXXX will work.
+ * . 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 0x0800 | '2' ;
+
+ case 'X': /* 0..9 */
+ return 0x0A00 | '0';
+
+ case 'Z': /* 1..9 */
+ return 0x0900 | '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;
+}
+
+/*!
+ * When looking up extensions, we can have different requests
+ * identified by the 'action' argument, as follows.
+ * Note that the coding is such that the low 4 bits are the
+ * third argument to extension_match_core.
+ */
+enum ext_match_t {
+ E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */
+ E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */
+ E_MATCH = 0x02, /* extension is an exact match */
+ E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */
+ E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */
+ E_FINDLABEL = 0x22 /* returns the priority for a given label. Requires exact match */
+};
+
+/*
+ * Internal function for ast_extension_{match|close}
+ * return 0 on no-match, 1 on match, 2 on early match.
+ * 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.
+ */
+
+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 ast_context *ast_context_find(const char *name)
+{
+ struct ast_context *tmp = NULL;
+
+ ast_rdlock_contexts();
+
+ 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);
+}
+
+/* request and result for pbx_find_extension */
+struct pbx_find_info {
+#if 0
+ const char *context;
+ const char *exten;
+ int priority;
+#endif
+
+ char *incstack[AST_PBX_MAX_STACK]; /* filled during the search */
+ int stacklen; /* modified during the search */
+ int status; /* set on return */
+ struct ast_switch *swo; /* set on return */
+ const char *data; /* set on return */
+ const char *foundcontext; /* set on return */
+};
+
+static 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;
+ struct ast_exten *e, *eroot;
+ struct ast_include *i;
+ struct ast_sw *sw;
+ char *tmpdata = NULL;
+
+ /* Initialize status if appropriate */
+ if (q->stacklen == 0) {
+ q->status = STATUS_NO_CONTEXT;
+ q->swo = NULL;
+ q->data = NULL;
+ q->foundcontext = NULL;
+ }
+ /* Check for stack overflow */
+ 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 */
+ tmp = NULL;
+ while ((tmp = ast_walk_contexts(tmp)) ) {
+ if (!strcmp(tmp->name, context))
+ break;
+ }
+ if (!tmp)
+ return NULL;
+ }
+ if (q->status < STATUS_NO_EXTENSION)
+ q->status = STATUS_NO_EXTENSION;
+
+ /* 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;
+ 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 */
+ }
+ 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) {
+ if (!(tmpdata = ast_threadstorage_get(&switch_data, 512))) {
+ ast_log(LOG_WARNING, "Can't evaluate switch?!");
+ continue;
+ }
+ pbx_substitute_variables_helper(chan, sw->data, tmpdata, 512);
+ }
+
+ /* 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 ? 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)))
+ return e;
+ if (q->swo)
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+/*! \brief extract offset:length from variable name.
+ * Returns 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.
+ *
+ * offset < 0 means start from the end of the string and set the beginning
+ * to be that many characters back.
+ * length is the length of the substring. A value less than 0 means to leave
+ * that many off the end.
+ * 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 pbx_retrieve_variable: Support for Asterisk built-in variables
+ ---*/
+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_mutex_lock(&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_mutex_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);
+}
+
+/*! \brief CLI function to show installed custom functions
+ \addtogroup CLI_functions
+ */
+static int handle_show_functions_deprecated(int fd, int argc, char *argv[])
+{
+ struct ast_custom_function *acf;
+ int count_acf = 0;
+ int like = 0;
+
+ if (argc == 4 && (!strcmp(argv[2], "like")) ) {
+ like = 1;
+ } else if (argc != 2) {
+ return RESULT_SHOWUSAGE;
+ }
+
+ ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
+
+ AST_LIST_LOCK(&acf_root);
+ AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
+ if (!like || strstr(acf->name, argv[3])) {
+ count_acf++;
+ ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
+ }
+ }
+ AST_LIST_UNLOCK(&acf_root);
+
+ ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
+
+ return RESULT_SUCCESS;
+}
+static int handle_show_functions(int fd, int argc, char *argv[])
+{
+ struct ast_custom_function *acf;
+ int count_acf = 0;
+ int like = 0;
+
+ if (argc == 5 && (!strcmp(argv[3], "like")) ) {
+ like = 1;
+ } else if (argc != 3) {
+ return RESULT_SHOWUSAGE;
+ }
+
+ ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
+
+ AST_LIST_LOCK(&acf_root);
+ AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
+ if (!like || strstr(acf->name, argv[4])) {
+ count_acf++;
+ ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
+ }
+ }
+ AST_LIST_UNLOCK(&acf_root);
+
+ ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_show_function_deprecated(int fd, int argc, char *argv[])
+{
+ 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;
+
+ if (argc < 3)
+ return RESULT_SHOWUSAGE;
+
+ if (!(acf = ast_custom_function_find(argv[2]))) {
+ ast_cli(fd, "No function by that name registered.\n");
+ return RESULT_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(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_show_function(int fd, int argc, char *argv[])
+{
+ 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;
+
+ if (argc < 4)
+ return RESULT_SHOWUSAGE;
+
+ if (!(acf = ast_custom_function_find(argv[3]))) {
+ ast_cli(fd, "No function by that name registered.\n");
+ return RESULT_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(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
+
+ return RESULT_SUCCESS;
+}
+
+static char *complete_show_function(const char *line, const char *word, int pos, int state)
+{
+ struct ast_custom_function *acf;
+ char *ret = NULL;
+ int which = 0;
+ int wordlen = strlen(word);
+
+ /* case-insensitive for convenience in this 'complete' function */
+ AST_LIST_LOCK(&acf_root);
+ AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
+ if (!strncasecmp(word, acf->name, wordlen) && ++which > state) {
+ ret = strdup(acf->name);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&acf_root);
+
+ return ret;
+}
+
+struct ast_custom_function *ast_custom_function_find(const char *name)
+{
+ struct ast_custom_function *acf = NULL;
+
+ AST_LIST_LOCK(&acf_root);
+ AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
+ if (!strcmp(name, acf->name))
+ break;
+ }
+ AST_LIST_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_LIST_LOCK(&acf_root);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
+ if (cur == acf) {
+ AST_LIST_REMOVE_CURRENT(&acf_root, acflist);
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Unregistered custom function %s\n", acf->name);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&acf_root);
+
+ return acf ? 0 : -1;
+}
+
+int ast_custom_function_register(struct ast_custom_function *acf)
+{
+ struct ast_custom_function *cur;
+
+ if (!acf)
+ return -1;
+
+ AST_LIST_LOCK(&acf_root);
+
+ if (ast_custom_function_find(acf->name)) {
+ ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
+ AST_LIST_UNLOCK(&acf_root);
+ return -1;
+ }
+
+ /* Store in alphabetical order */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
+ if (strcasecmp(acf->name, cur->name) < 0) {
+ AST_LIST_INSERT_BEFORE_CURRENT(&acf_root, acf, acflist);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ if (!cur)
+ AST_LIST_INSERT_TAIL(&acf_root, acf, acflist);
+
+ AST_LIST_UNLOCK(&acf_root);
+
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Registered custom function %s\n", acf->name);
+
+ 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, char *function, char *workspace, size_t len)
+{
+ char *args = func_args(function);
+ struct ast_custom_function *acfptr = ast_custom_function_find(function);
+
+ if (acfptr == NULL)
+ ast_log(LOG_ERROR, "Function %s not registered\n", function);
+ else if (!acfptr->read)
+ ast_log(LOG_ERROR, "Function %s cannot be read\n", function);
+ else
+ return acfptr->read(chan, function, args, workspace, len);
+ return -1;
+}
+
+int ast_func_write(struct ast_channel *chan, char *function, const char *value)
+{
+ char *args = func_args(function);
+ struct ast_custom_function *acfptr = ast_custom_function_find(function);
+
+ if (acfptr == NULL)
+ ast_log(LOG_ERROR, "Function %s not registered\n", function);
+ else if (!acfptr->write)
+ ast_log(LOG_ERROR, "Function %s cannot be written to\n", function);
+ else
+ return acfptr->write(chan, function, args, value);
+
+ 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, and assuming cp2 to be
+ zero-filled */
+ 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;
+
+ 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;
+ }
+
+ 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);
+
+ memset(ltmp, 0, 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");
+ }
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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;
+ }
+ } 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);
+
+ memset(ltmp, 0, 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);
+
+ if (length) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2);
+ count -= length;
+ cp2 += length;
+ }
+ }
+ }
+}
+
+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)
+{
+ memset(passdata, 0, datalen);
+
+ /* No variables or expressions in e->data, so why scan it? */
+ if (e->data && !strchr(e->data, '$') && !strstr(e->data,"${") && !strstr(e->data,"$[") && !strstr(e->data,"$(")) {
+ 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,
+ * and return 0 on success, -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)
+{
+ 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();
+ e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
+ if (e) {
+ 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 */
+ app = pbx_findapp(e->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);
+ if (option_debug) {
+ ast_log(LOG_DEBUG, "Launching '%s'\n", app->name);
+ }
+ if (option_verbose > 2) {
+ char tmp[80], tmp2[80], tmp3[EXT_DATA_SIZE];
+ ast_verbose( VERBOSE_PREFIX_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_CALL, "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)
+ ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
+ break;
+ case STATUS_NO_EXTENSION:
+ if (!matching_action)
+ ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
+ break;
+ case STATUS_NO_PRIORITY:
+ if (!matching_action)
+ 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)
+ ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
+ break;
+ default:
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Shouldn't happen!\n");
+ }
+
+ return (matching_action) ? 0 : -1;
+ }
+}
+
+/*! \brief ast_hint_extension: 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 ast_extensions_state2: 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 ast_extension_state2str: 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 ast_extension_state: 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 */
+}
+
+void ast_hint_state_changed(const char *device)
+{
+ struct ast_hint *hint;
+
+ AST_LIST_LOCK(&hints);
+
+ AST_LIST_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_LIST_UNLOCK(&hints);
+}
+
+/*! \brief ast_extension_state_add: 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_LIST_LOCK(&hints);
+
+ for (cblist = statecbs; cblist; cblist = cblist->next) {
+ if (cblist->callback == callback) {
+ cblist->data = data;
+ AST_LIST_UNLOCK(&hints);
+ return 0;
+ }
+ }
+
+ /* Now insert the callback */
+ if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
+ AST_LIST_UNLOCK(&hints);
+ return -1;
+ }
+ cblist->id = 0;
+ cblist->callback = callback;
+ cblist->data = data;
+
+ cblist->next = statecbs;
+ statecbs = cblist;
+
+ AST_LIST_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_LIST_LOCK(&hints);
+
+ AST_LIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == e)
+ break;
+ }
+
+ if (!hint) {
+ /* We have no hint, sorry */
+ AST_LIST_UNLOCK(&hints);
+ return -1;
+ }
+
+ /* Now insert the callback in the callback list */
+ if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
+ AST_LIST_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_LIST_UNLOCK(&hints);
+ return cblist->id;
+}
+
+/*! \brief ast_extension_state_del: 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_LIST_LOCK(&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_LIST_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;
+ free(cur);
+ ret = 0;
+ }
+ AST_LIST_UNLOCK(&hints);
+ return ret;
+}
+
+/*! \brief ast_add_hint: 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_LIST_LOCK(&hints);
+
+ /* Search if hint exists, do nothing */
+ AST_LIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == e) {
+ AST_LIST_UNLOCK(&hints);
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
+ return -1;
+ }
+ }
+
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
+
+ if (!(hint = ast_calloc(1, sizeof(*hint)))) {
+ AST_LIST_UNLOCK(&hints);
+ return -1;
+ }
+ /* Initialize and insert new item at the top */
+ hint->exten = e;
+ hint->laststate = ast_extension_state2(e);
+ AST_LIST_INSERT_HEAD(&hints, hint, list);
+
+ AST_LIST_UNLOCK(&hints);
+ return 0;
+}
+
+/*! \brief ast_change_hint: 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_LIST_LOCK(&hints);
+ AST_LIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == oe) {
+ hint->exten = ne;
+ res = 0;
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&hints);
+
+ return res;
+}
+
+/*! \brief ast_remove_hint: 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_LIST_LOCK(&hints);
+ AST_LIST_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);
+ free(cbprev);
+ }
+ hint->callbacks = NULL;
+ AST_LIST_REMOVE_CURRENT(&hints, list);
+ free(hint);
+ res = 0;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&hints);
+
+ return res;
+}
+
+
+/*! \brief ast_get_hint: 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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+int ast_spawn_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_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,
+ * return -1 on error, 0 on timeout or done.
+ */
+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 ? */
+ free(c->pbx);
+ }
+ if (!(c->pbx = ast_calloc(1, sizeof(*c->pbx))))
+ return -1;
+ /* 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' */
+ if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_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 */
+ if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_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));
+ }
+ }
+ 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 (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
+ found = 1;
+ if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
+ /* Something bad happened, or a hangup has been requested. */
+ if (strchr("0123456789ABCDEF*#", res)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
+ pos = 0;
+ dst_exten[pos++] = digit = res;
+ dst_exten[pos] = '\0';
+ break;
+ }
+ if (res == AST_PBX_KEEPALIVE) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
+ if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
+ error = 1;
+ break;
+ }
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+ if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+ 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 (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
+ c->_softhangup = 0;
+ } else 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) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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 (error)
+ break;
+
+ /* XXX we get here on non-existing extension or a keypress or hangup ? */
+
+ if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
+ /* If there is no match at priority 1, it is not a valid extension anymore.
+ * Try to continue at "i", 1 or exit if the latter does not exist.
+ */
+ if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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 {
+ 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";
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_2 "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)) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_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 {
+ 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)) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Timeout on %s\n", c->name);
+ set_ext_pri(c, "t", 1);
+ } 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) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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_test_flag(c, AST_FLAG_BRIDGE_HANGUP_RUN) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
+ set_ext_pri(c, "h", 1);
+ while(ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
+ if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
+ /* Something bad happened, or a hangup has been requested. */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+ if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+ break;
+ }
+ c->priority++;
+ }
+ }
+ ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
+ ast_clear_flag(c, AST_FLAG_BRIDGE_HANGUP_RUN); /* from one round to the next, make sure this gets cleared */
+ pbx_destroy(c->pbx);
+ c->pbx = NULL;
+ if (res != AST_PBX_KEEPALIVE)
+ ast_hangup(c);
+ return 0;
+}
+
+/* Returns 0 on success, non-zero if call limit was reached */
+static int increase_call_count(const struct ast_channel *c)
+{
+ int failed = 0;
+ double curloadavg;
+ 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 (!failed)
+ countcalls++;
+ 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->datad)
+ e->datad(e->data);
+ 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;
+ pthread_attr_t attr;
+
+ 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. */
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ast_pthread_create(&t, &attr, pbx_thread, c)) {
+ ast_log(LOG_WARNING, "Failed to create new channel thread\n");
+ pthread_attr_destroy(&attr);
+ decrease_call_count();
+ return AST_PBX_FAILED;
+ }
+ pthread_attr_destroy(&attr);
+
+ 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 pbx_set_autofallthrough(int newval)
+{
+ int oldval = autofallthrough;
+ autofallthrough = newval;
+ return oldval;
+}
+
+/* lookup for a context with a given name,
+ * return with conlock held if found, NULL if not found
+ */
+static struct ast_context *find_context_locked(const char *context)
+{
+ struct ast_context *c = NULL;
+
+ ast_rdlock_contexts();
+ while ( (c = ast_walk_contexts(c)) ) {
+ if (!strcmp(ast_get_context_name(c), context))
+ return c;
+ }
+ ast_unlock_contexts();
+
+ return NULL;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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.
+ *
+ * This function locks given context, removes include, unlock context and
+ * return.
+ */
+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_mutex_lock(&con->lock);
+
+ /* 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 */
+ free(i);
+ ret = 0;
+ break;
+ }
+ }
+
+ ast_mutex_unlock(&con->lock);
+ 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_mutex_lock(&con->lock);
+
+ /* 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(&con->alts, list);
+ free(i); /* free switch and return */
+ ret = 0;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ ast_mutex_unlock(&con->lock);
+
+ 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)
+{
+ return ast_context_remove_extension_callerid(context, extension, priority, NULL, 0, registrar);
+}
+
+int ast_context_remove_extension_callerid(const char *context, const char *extension, int priority, const char *callerid, int matchcid, 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_extension_callerid2(c, extension, priority, callerid, matchcid, 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)
+{
+ return ast_context_remove_extension_callerid2(con, extension, priority, NULL, 0, registrar);
+}
+
+int ast_context_remove_extension_callerid2(struct ast_context *con, const char *extension, int priority, const char *callerid, int matchcid, const char *registrar)
+{
+ struct ast_exten *exten, *prev_exten = NULL;
+ struct ast_exten *peer;
+ struct ast_exten *previous_peer = NULL;
+ struct ast_exten *next_peer = NULL;
+ int found = 0;
+
+ ast_mutex_lock(&con->lock);
+
+ /* scan the extension list to find first 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_mutex_unlock(&con->lock);
+ return -1;
+ }
+
+ /* scan the priority list to remove extension with exten->priority == priority */
+ for (peer = exten, next_peer = exten->peer ? exten->peer : exten->next;
+ peer && !strcmp(peer->exten, extension);
+ peer = next_peer, next_peer = next_peer ? (next_peer->peer ? next_peer->peer : next_peer->next) : NULL) {
+ if ((priority == 0 || peer->priority == priority) &&
+ (!callerid || !matchcid || (matchcid && !strcmp(peer->cidmatch, callerid))) &&
+ (!registrar || !strcmp(peer->registrar, registrar) )) {
+ found = 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 (!prev_exten) { /* change the root... */
+ con->root = next_node;
+ } else {
+ prev_exten->next = next_node; /* unlink */
+ }
+ if (peer->peer) { /* 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);
+ } else {
+ previous_peer = peer;
+ }
+ }
+ ast_mutex_unlock(&con->lock);
+ return found ? 0 : -1;
+}
+
+
+/*!
+ * \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;
+
+ ast_rdlock_contexts();
+
+ while ((c = ast_walk_contexts(c))) {
+ if (!strcmp(ast_get_context_name(c), context)) {
+ ret = 0;
+ break;
+ }
+ }
+
+ 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;
+
+ ast_rdlock_contexts();
+
+ while ((c = ast_walk_contexts(c))) {
+ if (!strcmp(ast_get_context_name(c), context)) {
+ ret = 0;
+ break;
+ }
+ }
+
+ 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_application(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description)
+{
+ struct ast_app *tmp, *cur = NULL;
+ char tmps[80];
+ int length;
+
+ AST_LIST_LOCK(&apps);
+ AST_LIST_TRAVERSE(&apps, tmp, list) {
+ if (!strcasecmp(app, tmp->name)) {
+ ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
+ AST_LIST_UNLOCK(&apps);
+ return -1;
+ }
+ }
+
+ length = sizeof(*tmp) + strlen(app) + 1;
+
+ if (!(tmp = ast_calloc(1, length))) {
+ AST_LIST_UNLOCK(&apps);
+ return -1;
+ }
+
+ strcpy(tmp->name, app);
+ tmp->execute = execute;
+ tmp->synopsis = synopsis;
+ tmp->description = description;
+
+ /* Store in alphabetical order */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
+ if (strcasecmp(tmp->name, cur->name) < 0) {
+ AST_LIST_INSERT_BEFORE_CURRENT(&apps, tmp, list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ if (!cur)
+ AST_LIST_INSERT_TAIL(&apps, tmp, list);
+
+ if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
+
+ AST_LIST_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_LIST_LOCK(&switches);
+ AST_LIST_TRAVERSE(&switches, tmp, list) {
+ if (!strcasecmp(tmp->name, sw->name)) {
+ AST_LIST_UNLOCK(&switches);
+ ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
+ return -1;
+ }
+ }
+ AST_LIST_INSERT_TAIL(&switches, sw, list);
+ AST_LIST_UNLOCK(&switches);
+
+ return 0;
+}
+
+void ast_unregister_switch(struct ast_switch *sw)
+{
+ AST_LIST_LOCK(&switches);
+ AST_LIST_REMOVE(&switches, sw, list);
+ AST_LIST_UNLOCK(&switches);
+}
+
+/*
+ * Help for CLI commands ...
+ */
+static char show_applications_help[] =
+"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";
+
+static char show_functions_help[] =
+"Usage: core show functions [like <text>]\n"
+" List builtin functions, optionally only those matching a given string\n";
+
+static char show_switches_help[] =
+"Usage: core show switches\n"
+" List registered switches\n";
+
+static char show_hints_help[] =
+"Usage: core show hints\n"
+" List registered hints\n";
+
+static char show_globals_help[] =
+"Usage: core show globals\n"
+" List current global dialplan variables and their values\n";
+
+static char show_application_help[] =
+"Usage: core show application <application> [<application> [<application> [...]]]\n"
+" Describes a particular application.\n";
+
+static char show_function_help[] =
+"Usage: core show function <function>\n"
+" Describe a particular dialplan function.\n";
+
+static char show_dialplan_help[] =
+"Usage: dialplan show [exten@][context]\n"
+" Show dialplan\n";
+
+static char set_global_help[] =
+"Usage: core set global <name> <value>\n"
+" Set global dialplan variable <name> to <value>\n";
+
+
+/*
+ * \brief 'show application' CLI command implementation functions ...
+ */
+
+/*
+ * 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 ...
+ */
+static char *complete_show_application(const char *line, const char *word, int pos, int state)
+{
+ struct ast_app *a;
+ char *ret = NULL;
+ int which = 0;
+ int wordlen = strlen(word);
+
+ /* return the n-th [partial] matching entry */
+ AST_LIST_LOCK(&apps);
+ AST_LIST_TRAVERSE(&apps, a, list) {
+ if (!strncasecmp(word, a->name, wordlen) && ++which > state) {
+ ret = strdup(a->name);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&apps);
+
+ return ret;
+}
+
+static int handle_show_application_deprecated(int fd, int argc, char *argv[])
+{
+ struct ast_app *a;
+ int app, no_registered_app = 1;
+
+ if (argc < 3)
+ return RESULT_SHOWUSAGE;
+
+ /* ... go through all applications ... */
+ AST_LIST_LOCK(&apps);
+ AST_LIST_TRAVERSE(&apps, a, list) {
+ /* ... compare this application name with all arguments given
+ * to 'show application' command ... */
+ for (app = 2; app < argc; app++) {
+ if (!strcasecmp(a->name, 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 (a->synopsis)
+ synopsis_size = strlen(a->synopsis) + 23;
+ else
+ synopsis_size = strlen("Not available") + 23;
+ synopsis = alloca(synopsis_size);
+
+ if (a->description)
+ description_size = strlen(a->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", a->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,
+ a->synopsis ? a->synopsis : "Not available",
+ COLOR_CYAN, 0, synopsis_size);
+ term_color(description,
+ a->description ? a->description : "Not available",
+ COLOR_CYAN, 0, description_size);
+
+ ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
+ } else {
+ /* ... one of our applications, show info ...*/
+ ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
+ "[Synopsis]\n %s\n\n"
+ "[Description]\n%s\n",
+ a->name,
+ a->synopsis ? a->synopsis : "Not available",
+ a->description ? a->description : "Not available");
+ }
+ }
+ }
+ }
+ AST_LIST_UNLOCK(&apps);
+
+ /* we found at least one app? no? */
+ if (no_registered_app) {
+ ast_cli(fd, "Your application(s) is (are) not registered\n");
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_show_application(int fd, int argc, char *argv[])
+{
+ struct ast_app *a;
+ int app, no_registered_app = 1;
+
+ if (argc < 4)
+ return RESULT_SHOWUSAGE;
+
+ /* ... go through all applications ... */
+ AST_LIST_LOCK(&apps);
+ AST_LIST_TRAVERSE(&apps, a, list) {
+ /* ... compare this application name with all arguments given
+ * to 'show application' command ... */
+ for (app = 3; app < argc; app++) {
+ if (!strcasecmp(a->name, 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 (a->synopsis)
+ synopsis_size = strlen(a->synopsis) + 23;
+ else
+ synopsis_size = strlen("Not available") + 23;
+ synopsis = alloca(synopsis_size);
+
+ if (a->description)
+ description_size = strlen(a->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", a->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,
+ a->synopsis ? a->synopsis : "Not available",
+ COLOR_CYAN, 0, synopsis_size);
+ term_color(description,
+ a->description ? a->description : "Not available",
+ COLOR_CYAN, 0, description_size);
+
+ ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
+ } else {
+ /* ... one of our applications, show info ...*/
+ ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
+ "[Synopsis]\n %s\n\n"
+ "[Description]\n%s\n",
+ a->name,
+ a->synopsis ? a->synopsis : "Not available",
+ a->description ? a->description : "Not available");
+ }
+ }
+ }
+ }
+ AST_LIST_UNLOCK(&apps);
+
+ /* we found at least one app? no? */
+ if (no_registered_app) {
+ ast_cli(fd, "Your application(s) is (are) not registered\n");
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+/*! \brief handle_show_hints: CLI support for listing registred dial plan hints */
+static int handle_show_hints(int fd, int argc, char *argv[])
+{
+ struct ast_hint *hint;
+ int num = 0;
+ int watchers;
+ struct ast_state_cb *watcher;
+
+ if (AST_LIST_EMPTY(&hints)) {
+ ast_cli(fd, "There are no registered dialplan hints\n");
+ return RESULT_SUCCESS;
+ }
+ /* ... we have hints ... */
+ ast_cli(fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
+ AST_LIST_LOCK(&hints);
+ AST_LIST_TRAVERSE(&hints, hint, list) {
+ watchers = 0;
+ for (watcher = hint->callbacks; watcher; watcher = watcher->next)
+ watchers++;
+ ast_cli(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(fd, "----------------\n");
+ ast_cli(fd, "- %d hints registered\n", num);
+ AST_LIST_UNLOCK(&hints);
+ return RESULT_SUCCESS;
+}
+
+/*! \brief handle_show_switches: CLI support for listing registred dial plan switches */
+static int handle_show_switches(int fd, int argc, char *argv[])
+{
+ struct ast_switch *sw;
+
+ AST_LIST_LOCK(&switches);
+
+ if (AST_LIST_EMPTY(&switches)) {
+ AST_LIST_UNLOCK(&switches);
+ ast_cli(fd, "There are no registered alternative switches\n");
+ return RESULT_SUCCESS;
+ }
+
+ ast_cli(fd, "\n -= Registered Asterisk Alternative Switches =-\n");
+ AST_LIST_TRAVERSE(&switches, sw, list)
+ ast_cli(fd, "%s: %s\n", sw->name, sw->description);
+
+ AST_LIST_UNLOCK(&switches);
+
+ return RESULT_SUCCESS;
+}
+
+/*
+ * 'show applications' CLI command implementation functions ...
+ */
+static int handle_show_applications_deprecated(int fd, int argc, char *argv[])
+{
+ struct ast_app *a;
+ int like = 0, describing = 0;
+ int total_match = 0; /* Number of matches in like clause */
+ int total_apps = 0; /* Number of apps registered */
+
+ AST_LIST_LOCK(&apps);
+
+ if (AST_LIST_EMPTY(&apps)) {
+ ast_cli(fd, "There are no registered applications\n");
+ AST_LIST_UNLOCK(&apps);
+ return -1;
+ }
+
+ /* show applications like <keyword> */
+ if ((argc == 4) && (!strcmp(argv[2], "like"))) {
+ like = 1;
+ } else if ((argc > 3) && (!strcmp(argv[2], "describing"))) {
+ describing = 1;
+ }
+
+ /* show applications describing <keyword1> [<keyword2>] [...] */
+ if ((!like) && (!describing)) {
+ ast_cli(fd, " -= Registered Asterisk Applications =-\n");
+ } else {
+ ast_cli(fd, " -= Matching Asterisk Applications =-\n");
+ }
+
+ AST_LIST_TRAVERSE(&apps, a, list) {
+ int printapp = 0;
+ total_apps++;
+ if (like) {
+ if (strcasestr(a->name, argv[3])) {
+ printapp = 1;
+ total_match++;
+ }
+ } else if (describing) {
+ if (a->description) {
+ /* Match all words on command line */
+ int i;
+ printapp = 1;
+ for (i = 3; i < argc; i++) {
+ if (!strcasestr(a->description, argv[i])) {
+ printapp = 0;
+ } else {
+ total_match++;
+ }
+ }
+ }
+ } else {
+ printapp = 1;
+ }
+
+ if (printapp) {
+ ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
+ }
+ }
+ if ((!like) && (!describing)) {
+ ast_cli(fd, " -= %d Applications Registered =-\n",total_apps);
+ } else {
+ ast_cli(fd, " -= %d Applications Matching =-\n",total_match);
+ }
+
+ AST_LIST_UNLOCK(&apps);
+
+ return RESULT_SUCCESS;
+}
+static int handle_show_applications(int fd, int argc, char *argv[])
+{
+ struct ast_app *a;
+ int like = 0, describing = 0;
+ int total_match = 0; /* Number of matches in like clause */
+ int total_apps = 0; /* Number of apps registered */
+
+ AST_LIST_LOCK(&apps);
+
+ if (AST_LIST_EMPTY(&apps)) {
+ ast_cli(fd, "There are no registered applications\n");
+ AST_LIST_UNLOCK(&apps);
+ return -1;
+ }
+
+ /* core list applications like <keyword> */
+ if ((argc == 5) && (!strcmp(argv[3], "like"))) {
+ like = 1;
+ } else if ((argc > 4) && (!strcmp(argv[3], "describing"))) {
+ describing = 1;
+ }
+
+ /* core list applications describing <keyword1> [<keyword2>] [...] */
+ if ((!like) && (!describing)) {
+ ast_cli(fd, " -= Registered Asterisk Applications =-\n");
+ } else {
+ ast_cli(fd, " -= Matching Asterisk Applications =-\n");
+ }
+
+ AST_LIST_TRAVERSE(&apps, a, list) {
+ int printapp = 0;
+ total_apps++;
+ if (like) {
+ if (strcasestr(a->name, argv[4])) {
+ printapp = 1;
+ total_match++;
+ }
+ } else if (describing) {
+ if (a->description) {
+ /* Match all words on command line */
+ int i;
+ printapp = 1;
+ for (i = 4; i < argc; i++) {
+ if (!strcasestr(a->description, argv[i])) {
+ printapp = 0;
+ } else {
+ total_match++;
+ }
+ }
+ }
+ } else {
+ printapp = 1;
+ }
+
+ if (printapp) {
+ ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
+ }
+ }
+ if ((!like) && (!describing)) {
+ ast_cli(fd, " -= %d Applications Registered =-\n",total_apps);
+ } else {
+ ast_cli(fd, " -= %d Applications Matching =-\n",total_match);
+ }
+
+ AST_LIST_UNLOCK(&apps);
+
+ return RESULT_SUCCESS;
+}
+
+static char *complete_show_applications_deprecated(const char *line, const char *word, int pos, int state)
+{
+ static char* choices[] = { "like", "describing", NULL };
+
+ return (pos != 2) ? NULL : ast_cli_complete(word, choices, state);
+}
+
+static char *complete_show_applications(const char *line, const char *word, int pos, int state)
+{
+ static char* choices[] = { "like", "describing", NULL };
+
+ return (pos != 3) ? NULL : ast_cli_complete(word, choices, state);
+}
+
+/*
+ * '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;
+}
+
+struct dialplan_counters {
+ 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_lock_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 */
+ 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));
+ }
+ }
+
+ 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 int handle_show_dialplan(int fd, int argc, char *argv[])
+{
+ char *exten = NULL, *context = NULL;
+ /* Variables used for different counters */
+ struct dialplan_counters counters;
+
+ const char *incstack[AST_PBX_MAX_STACK];
+ memset(&counters, 0, sizeof(counters));
+
+ if (argc != 2 && argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ /* we obtain [exten@]context? if yes, split them ... */
+ if (argc == 3) {
+ if (strchr(argv[2], '@')) { /* split into exten & context */
+ context = ast_strdupa(argv[2]);
+ exten = strsep(&context, "@");
+ /* change empty strings to NULL */
+ if (ast_strlen_zero(exten))
+ exten = NULL;
+ } else { /* no '@' char, only context given */
+ context = argv[2];
+ }
+ if (ast_strlen_zero(context))
+ context = NULL;
+ }
+ /* else Show complete dial plan, context and exten are NULL */
+ show_dialplan_helper(fd, context, exten, &counters, NULL, 0, incstack);
+
+ /* check for input failure and throw some error messages */
+ if (context && !counters.context_existence) {
+ ast_cli(fd, "There is no existence of '%s' context\n", context);
+ return RESULT_FAILURE;
+ }
+
+ if (exten && !counters.extension_existence) {
+ if (context)
+ ast_cli(fd, "There is no existence of %s@%s extension\n",
+ exten, context);
+ else
+ ast_cli(fd,
+ "There is no existence of '%s' extension in all contexts\n",
+ exten);
+ return RESULT_FAILURE;
+ }
+
+ ast_cli(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 RESULT_SUCCESS;
+}
+
+/*! \brief CLI support for listing global variables in a parseable way */
+static int handle_show_globals(int fd, int argc, char *argv[])
+{
+ int i = 0;
+ struct ast_var_t *newvariable;
+
+ ast_mutex_lock(&globalslock);
+ AST_LIST_TRAVERSE (&globals, newvariable, entries) {
+ i++;
+ ast_cli(fd, " %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
+ }
+ ast_mutex_unlock(&globalslock);
+ ast_cli(fd, "\n -- %d variables\n", i);
+
+ return RESULT_SUCCESS;
+}
+
+/*! \brief CLI support for setting global variables */
+static int handle_set_global_deprecated(int fd, int argc, char *argv[])
+{
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ pbx_builtin_setvar_helper(NULL, argv[2], argv[3]);
+ ast_cli(fd, "\n -- Global variable %s set to %s\n", argv[2], argv[3]);
+
+ return RESULT_SUCCESS;
+}
+
+
+static int handle_set_global(int fd, int argc, char *argv[])
+{
+ if (argc != 5)
+ return RESULT_SHOWUSAGE;
+
+ pbx_builtin_setvar_helper(NULL, argv[3], argv[4]);
+ ast_cli(fd, "\n -- Global variable %s set to %s\n", argv[3], argv[4]);
+
+ return RESULT_SUCCESS;
+}
+
+
+
+/*
+ * CLI entries for upper commands ...
+ */
+static struct ast_cli_entry cli_show_applications_deprecated = {
+ { "show", "applications", NULL },
+ handle_show_applications_deprecated, NULL,
+ NULL, complete_show_applications_deprecated };
+
+static struct ast_cli_entry cli_show_functions_deprecated = {
+ { "show", "functions", NULL },
+ handle_show_functions_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_switches_deprecated = {
+ { "show", "switches", NULL },
+ handle_show_switches, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_hints_deprecated = {
+ { "show", "hints", NULL },
+ handle_show_hints, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_globals_deprecated = {
+ { "show", "globals", NULL },
+ handle_show_globals, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_function_deprecated = {
+ { "show" , "function", NULL },
+ handle_show_function_deprecated, NULL,
+ NULL, complete_show_function };
+
+static struct ast_cli_entry cli_show_application_deprecated = {
+ { "show", "application", NULL },
+ handle_show_application_deprecated, NULL,
+ NULL, complete_show_application };
+
+static struct ast_cli_entry cli_show_dialplan_deprecated = {
+ { "show", "dialplan", NULL },
+ handle_show_dialplan, NULL,
+ NULL, complete_show_dialplan_context };
+
+static struct ast_cli_entry cli_set_global_deprecated = {
+ { "set", "global", NULL },
+ handle_set_global_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry pbx_cli[] = {
+ { { "core", "show", "applications", NULL },
+ handle_show_applications, "Shows registered dialplan applications",
+ show_applications_help, complete_show_applications, &cli_show_applications_deprecated },
+
+ { { "core", "show", "functions", NULL },
+ handle_show_functions, "Shows registered dialplan functions",
+ show_functions_help, NULL, &cli_show_functions_deprecated },
+
+ { { "core", "show", "switches", NULL },
+ handle_show_switches, "Show alternative switches",
+ show_switches_help, NULL, &cli_show_switches_deprecated },
+
+ { { "core", "show", "hints", NULL },
+ handle_show_hints, "Show dialplan hints",
+ show_hints_help, NULL, &cli_show_hints_deprecated },
+
+ { { "core", "show", "globals", NULL },
+ handle_show_globals, "Show global dialplan variables",
+ show_globals_help, NULL, &cli_show_globals_deprecated },
+
+ { { "core", "show" , "function", NULL },
+ handle_show_function, "Describe a specific dialplan function",
+ show_function_help, complete_show_function, &cli_show_function_deprecated },
+
+ { { "core", "show", "application", NULL },
+ handle_show_application, "Describe a specific dialplan application",
+ show_application_help, complete_show_application, &cli_show_application_deprecated },
+
+ { { "core", "set", "global", NULL },
+ handle_set_global, "Set global dialplan variable",
+ set_global_help, NULL, &cli_set_global_deprecated },
+
+ { { "dialplan", "show", NULL },
+ handle_show_dialplan, "Show dialplan",
+ show_dialplan_help, complete_show_dialplan_context, &cli_show_dialplan_deprecated },
+};
+
+int ast_unregister_application(const char *app)
+{
+ struct ast_app *tmp;
+
+ AST_LIST_LOCK(&apps);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, tmp, list) {
+ if (!strcasecmp(app, tmp->name)) {
+ AST_LIST_REMOVE_CURRENT(&apps, list);
+ if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name);
+ free(tmp);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_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;
+ int length = sizeof(struct ast_context) + strlen(name) + 1;
+
+ if (!extcontexts) {
+ ast_rdlock_contexts();
+ local_contexts = &contexts;
+ } else
+ 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;
+ }
+ if (!extcontexts)
+ ast_unlock_contexts();
+ return tmp;
+ }
+ }
+
+ if (!extcontexts)
+ ast_unlock_contexts();
+
+ if ((tmp = ast_calloc(1, length))) {
+ ast_mutex_init(&tmp->lock);
+ ast_mutex_init(&tmp->macrolock);
+ strcpy(tmp->name, name);
+ tmp->registrar = registrar;
+ if (!extcontexts)
+ ast_wrlock_contexts();
+ tmp->next = *local_contexts;
+ *local_contexts = tmp;
+ if (!extcontexts)
+ ast_unlock_contexts();
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name);
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_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_LIST_LOCK(&hints);
+
+ /* preserve all watchers for hints associated with this registrar */
+ AST_LIST_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 */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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;
+ }
+ }
+ 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_LIST_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);
+ free(prevcb);
+ }
+ } else {
+ thiscb = this->callbacks;
+ while (thiscb->next)
+ thiscb = thiscb->next;
+ thiscb->next = hint->callbacks;
+ hint->callbacks = this->callbacks;
+ hint->laststate = this->laststate;
+ }
+ free(this);
+ }
+
+ AST_LIST_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 tm tm;
+ time_t t = time(NULL);
+
+ ast_localtime(&t, &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_mutex_lock(&con->lock);
+
+ /* ... 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)) {
+ free(new_include);
+ ast_mutex_unlock(&con->lock);
+ 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;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
+ ast_mutex_unlock(&con->lock);
+
+ return 0;
+}
+
+/*
+ * errno values
+ * EBUSY - can't lock
+ * 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++;
+
+ /* 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++;
+ }
+ new_sw->eval = eval;
+ new_sw->registrar = registrar;
+
+ /* ... try to lock this context ... */
+ ast_mutex_lock(&con->lock);
+
+ /* ... 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)) {
+ free(new_sw);
+ ast_mutex_unlock(&con->lock);
+ errno = EEXIST;
+ return -1;
+ }
+ }
+
+ /* ... sw new context into context list, unlock, return */
+ AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
+
+ ast_mutex_unlock(&con->lock);
+
+ return 0;
+}
+
+/*
+ * EBUSY - can't lock
+ * 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_mutex_lock(&con->lock);
+
+ for (ip = con->ignorepats; ip; ip = ip->next) {
+ if (!strcmp(ip->pattern, ignorepat) &&
+ (!registrar || (registrar == ip->registrar))) {
+ if (ipl) {
+ ipl->next = ip->next;
+ free(ip);
+ } else {
+ con->ignorepats = ip->next;
+ free(ip);
+ }
+ ast_mutex_unlock(&con->lock);
+ return 0;
+ }
+ ipl = ip;
+ }
+
+ ast_mutex_unlock(&con->lock);
+ 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;
+ char *pattern;
+ 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. Also, gcc 4.2
+ * sees the cast as dereferencing a type-punned pointer and warns about
+ * it. This is the workaround (we're telling gcc, yes, that's really
+ * what we wanted to do).
+ */
+ pattern = (char *) ignorepat->pattern;
+ strcpy(pattern, value);
+ ignorepat->next = NULL;
+ ignorepat->registrar = registrar;
+ ast_mutex_lock(&con->lock);
+ for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
+ ignorepatl = ignorepatc;
+ if (!strcasecmp(ignorepatc->pattern, value)) {
+ /* Already there */
+ ast_mutex_unlock(&con->lock);
+ errno = EEXIST;
+ return -1;
+ }
+ }
+ if (ignorepatl)
+ ignorepatl->next = ignorepat;
+ else
+ con->ignorepats = ignorepat;
+ ast_mutex_unlock(&con->lock);
+ 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 (!tmpchan) {
+ res = -1;
+ } else {
+ if (chan->cdr) {
+ ast_cdr_discard(tmpchan->cdr);
+ tmpchan->cdr = ast_cdr_dup(chan->cdr); /* share the love */
+ }
+ /* 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 */
+ if (ast_channel_masquerade(tmpchan, chan)) {
+ /* Failed to set up the masquerade. It's probably chan_local
+ * in the middle of optimizing itself out. Sad. :( */
+ ast_hangup(tmpchan);
+ tmpchan = NULL;
+ res = -1;
+ } else {
+ /* 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.
+ * returns 0 on success, -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;
+
+ 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 */
+ 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);
+ 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 */
+ ep->peer = tmp;
+ else if (el) /* We're the first extension. Take over e's functions */
+ el->next = tmp;
+ else /* We're the very first extension. */
+ con->root = tmp;
+ if (tmp->priority == PRIORITY_HINT)
+ ast_change_hint(e,tmp);
+ /* Destroy the old one */
+ if (e->datad)
+ e->datad(e->data);
+ 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 */
+ ep->peer = tmp;
+ else { /* we are the first in some peer list, so link in the ext list */
+ 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, *e, *el = NULL;
+ int res;
+ int length;
+ char *p;
+ char expand_buf[VAR_BUF_SIZE] = { 0, };
+
+ /* if we are adding a hint, and there are global variables, and the hint
+ contains variable references, then expand them
+ */
+ ast_mutex_lock(&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_mutex_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_mutex_lock(&con->lock);
+ 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, tmp->exten);
+ 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_mutex_unlock(&con->lock);
+ 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)
+ el->next = tmp;
+ else
+ con->root = tmp;
+ ast_mutex_unlock(&con->lock);
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+ }
+ if (option_debug) {
+ if (tmp->matchcid) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n",
+ tmp->exten, tmp->priority, tmp->cidmatch, con->name);
+ } else {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n",
+ tmp->exten, tmp->priority, con->name);
+ }
+ }
+ if (option_verbose > 2) {
+ if (tmp->matchcid) {
+ ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n",
+ tmp->exten, tmp->priority, tmp->cidmatch, con->name);
+ } else {
+ ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n",
+ tmp->exten, tmp->priority, con->name);
+ }
+ }
+ 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) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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;
+ }
+ }
+ }
+ free(as);
+ if (chan)
+ ast_hangup(chan);
+ return NULL;
+}
+
+/*! Function to post an empty cdr after a spool call fails.
+ *
+ * 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, "%s", "");
+
+ 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); /* initilize 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 */
+ chan->cdr = NULL;
+ 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;
+ pthread_attr_t attr;
+
+ if (sync) {
+ LOAD_OH(oh);
+ 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;
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_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);
+ chan = NULL;
+ 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;
+ }
+ chan = NULL;
+ }
+ } else {
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_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);
+ chan = NULL;
+ }
+ }
+
+ 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);
+ if (ast_pbx_run(chan)) {
+ ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
+ ast_hangup(chan);
+ }
+ chan = NULL;
+ }
+ }
+ }
+ } 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) {
+ 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);
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
+ ast_log(LOG_WARNING, "Failed to start async wait\n");
+ free(as);
+ if (channel) {
+ *channel = NULL;
+ ast_channel_unlock(chan);
+ }
+ ast_hangup(chan);
+ res = -1;
+ pthread_attr_destroy(&attr);
+ goto outgoing_exten_cleanup;
+ }
+ pthread_attr_destroy(&attr);
+ 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) {
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_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);
+ 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;
+ pthread_attr_t attr;
+
+ 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) {
+ ast_set_variables(chan, vars);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ if (chan->_state == AST_STATE_UP) {
+ res = 0;
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_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 {
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (locked_channel)
+ ast_channel_lock(chan);
+ if (ast_pthread_create(&tmp->t, &attr, ast_pbx_run_app, tmp)) {
+ ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", chan->name, strerror(errno));
+ free(tmp);
+ if (locked_channel)
+ ast_channel_unlock(chan);
+ ast_hangup(chan);
+ res = -1;
+ } else {
+ if (locked_channel)
+ *locked_channel = chan;
+ }
+ pthread_attr_destroy(&attr);
+ }
+ }
+ } else {
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_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) {
+ 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. */
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (locked_channel)
+ ast_channel_lock(chan);
+ if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
+ ast_log(LOG_WARNING, "Failed to start async wait\n");
+ free(as);
+ if (locked_channel)
+ ast_channel_unlock(chan);
+ ast_hangup(chan);
+ res = -1;
+ pthread_attr_destroy(&attr);
+ goto outgoing_app_cleanup;
+ } else {
+ if (locked_channel)
+ *locked_channel = chan;
+ }
+ pthread_attr_destroy(&attr);
+ 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) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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_mutex_lock(&tmp->lock);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "delete ctx %s %s\n", tmp->name, tmp->registrar);
+ 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_mutex_unlock(&tmp->lock);
+ for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
+ struct ast_include *tmpil = tmpi;
+ tmpi = tmpi->next;
+ free(tmpil);
+ }
+ for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
+ struct ast_ignorepat *ipl = ipi;
+ ipi = ipi->next;
+ free(ipl);
+ }
+ while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
+ 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_mutex_destroy(&tmp->lock);
+ 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;
+ int waittime;
+
+ if (ast_strlen_zero(data) || (sscanf(data, "%d", &waittime) != 1) || (waittime < 0))
+ waittime = -1;
+ if (waittime > -1) {
+ ast_safe_sleep(chan, waittime * 1000);
+ } 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;
+ int res;
+
+ if (chan->_state == AST_STATE_UP)
+ delay = 0;
+ else if (!ast_strlen_zero(data))
+ delay = atoi(data);
+
+ res = ast_answer(chan);
+ if (res)
+ return res;
+
+ if (delay)
+ res = ast_safe_sleep(chan, delay);
+
+ return res;
+}
+
+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, '|')))
+ *s++ = '\0';
+
+ 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) {
+ 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 sec;
+ 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]));
+
+ /* Wait for "n" seconds */
+ if (args.timeout && (sec = atof(args.timeout)) > 0.0)
+ ms = 1000 * sec;
+ 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)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, continuing...\n", chan->name);
+ } else if (chan->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Call timeout on %s, checking for 'T'\n", chan->name);
+ res = -1;
+ } else if (ast_exists_extension(chan, chan->context, "t", 1, chan->cid.cid_num)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, going to 't'\n", chan->name);
+ set_ext_pri(chan, "t", 0); /* 0 will become 1, next time through the loop */
+ } 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);
+
+ return res;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_background(struct ast_channel *chan, void *data)
+{
+ int res = 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)) {
+ return 0;
+ } 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;
+ 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;
+ }
+ 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 && (option_verbose > 2))
+ ast_verbose( VERBOSE_PREFIX_3 "Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1);
+ return res;
+}
+
+
+int pbx_builtin_serialize_variables(struct ast_channel *chan, char *buf, size_t size)
+{
+ struct ast_var_t *variables;
+ const char *var, *val;
+ int total = 0;
+
+ if (!chan)
+ return 0;
+
+ memset(buf, 0, size);
+
+ 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_build_string(&buf, &size, "%s=%s\n", var, val)) {
+ 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_mutex_lock(&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_mutex_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_mutex_lock(&globalslock);
+ headp = &globals;
+ }
+
+ if (value) {
+ if ((option_verbose > 1) && (headp == &globals))
+ ast_verbose(VERBOSE_PREFIX_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_mutex_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_mutex_lock(&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 ((option_verbose > 1) && (headp == &globals))
+ ast_verbose(VERBOSE_PREFIX_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_mutex_unlock(&globalslock);
+}
+
+int pbx_builtin_setvar(struct ast_channel *chan, void *data)
+{
+ char *name, *value, *mydata;
+ int argc;
+ char *argv[24]; /* this will only support a maximum of 24 variables being set in a single operation */
+ int global = 0;
+ int x;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Set requires at least one variable name/value pair.\n");
+ return 0;
+ }
+
+ mydata = ast_strdupa(data);
+ argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
+
+ /* check for a trailing flags argument */
+ if ((argc > 1) && !strchr(argv[argc-1], '=')) {
+ argc--;
+ if (strchr(argv[argc], 'g')) {
+ ast_log(LOG_WARNING, "The use of the 'g' flag is deprecated. Please use Set(GLOBAL(foo)=bar) instead\n");
+ global = 1;
+ }
+ }
+
+ if (argc > 1)
+ ast_log(LOG_WARNING, "Setting multiple variables at once within Set is deprecated. Please separate each name/value pair into its own line.\n");
+
+ for (x = 0; x < argc; x++) {
+ name = argv[x];
+ if ((value = strchr(name, '='))) {
+ *value++ = '\0';
+ pbx_builtin_setvar_helper((global) ? NULL : chan, name, value);
+ } else
+ ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name);
+ }
+
+ return(0);
+}
+
+int pbx_builtin_importvar(struct ast_channel *chan, void *data)
+{
+ char *name;
+ char *value;
+ char *channel;
+ char tmp[VAR_BUF_SIZE]="";
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
+ return 0;
+ }
+
+ 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);
+}
+
+/*! \todo XXX overwrites data ? */
+static int pbx_builtin_setglobalvar(struct ast_channel *chan, void *data)
+{
+ char *name;
+ char *stringp = data;
+ static int dep_warning = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
+ return 0;
+ }
+
+ name = strsep(&stringp, "=");
+
+ if (!dep_warning) {
+ dep_warning = 1;
+ ast_log(LOG_WARNING, "SetGlobalVar is deprecated. Please use Set(GLOBAL(%s)=%s) instead.\n", name, stringp);
+ }
+
+ /*! \todo XXX watch out, leading whitespace ? */
+ pbx_builtin_setvar_helper(NULL, name, stringp);
+
+ 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_mutex_lock(&globalslock);
+ while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
+ ast_var_delete(vardata);
+ ast_mutex_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;
+ int rc;
+ 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)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Not taking any branch\n");
+ return 0;
+ }
+
+ rc = pbx_builtin_goto(chan, branch);
+
+ return rc;
+}
+
+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;
+ }
+ }
+
+ if (ast_say_number(chan, atoi(tmp), "", chan->language, options)) {
+ ast_log(LOG_WARNING, "We were unable to say the number %s, is it too large?\n", tmp);
+ }
+
+ return 0;
+}
+
+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;
+}
+
+int load_pbx(void)
+{
+ int x;
+
+ /* Initialize the PBX */
+ if (option_verbose) {
+ ast_verbose( "Asterisk PBX Core Initializing\n");
+ ast_verbose( "Registering builtin applications:\n");
+ }
+ ast_cli_register_multiple(pbx_cli, sizeof(pbx_cli) / sizeof(struct ast_cli_entry));
+
+ /* Register builtin applications */
+ for (x=0; x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
+ if (option_verbose)
+ ast_verbose( VERBOSE_PREFIX_1 "[%s]\n", builtins[x].name);
+ if (ast_register_application(builtins[x].name, builtins[x].execute, builtins[x].synopsis, builtins[x].description)) {
+ ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Lock context list functions ...
+ */
+int ast_lock_contexts()
+{
+ return ast_rwlock_wrlock(&conlock);
+}
+
+int ast_rdlock_contexts(void)
+{
+ return ast_rwlock_rdlock(&conlock);
+}
+
+int ast_wrlock_contexts(void)
+{
+ return ast_rwlock_wrlock(&conlock);
+}
+
+int ast_unlock_contexts()
+{
+ return ast_rwlock_unlock(&conlock);
+}
+
+/*
+ * Lock context ...
+ */
+int ast_lock_context(struct ast_context *con)
+{
+ return ast_mutex_lock(&con->lock);
+}
+
+int ast_unlock_context(struct ast_context *con)
+{
+ return ast_mutex_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))
+ continue;
+
+ res = -1;
+ ast_log(LOG_WARNING, "Context '%s' tries to include nonexistent context '%s'\n",
+ ast_get_context_name(con), inc->rname);
+ break;
+ }
+
+ 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 (optional context|optional 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);
+ return 0;
+
+}
diff --git a/main/plc.c b/main/plc.c
new file mode 100644
index 000000000..336a99030
--- /dev/null
+++ b/main/plc.c
@@ -0,0 +1,251 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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/main/poll.c b/main/poll.c
new file mode 100644
index 000000000..c053ba015
--- /dev/null
+++ b/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 = 0; /* maximum fd value */
+ struct timeval *pTimeout; /* actually passed */
+
+ FD_ZERO (&read_descs);
+ FD_ZERO (&write_descs);
+ FD_ZERO (&except_descs);
+
+ /* Map the poll() file descriptor list in the select() data structures. */
+
+ if (pArray) {
+ 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/main/privacy.c b/main/privacy.c
new file mode 100644
index 000000000..b27bb5046
--- /dev/null
+++ b/main/privacy.c
@@ -0,0 +1,119 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.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/main/rtp.c b/main/rtp.c
new file mode 100644
index 000000000..28fcad75f
--- /dev/null
+++ b/main/rtp.c
@@ -0,0 +1,3833 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+
+#include "asterisk/rtp.h"
+#include "asterisk/frame.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/acl.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+#include "asterisk/unaligned.h"
+#include "asterisk/utils.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 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;
+ ast_mutex_t bridge_lock;
+ 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 */
+ int set_marker_bit:1; /*!< Whether to set the marker bit or not */
+};
+
+/* 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;
+};
+
+
+typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id;
+
+/* XXX Maybe stun belongs in another file if it ever has use outside of RTP */
+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));
+
+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)
+
+#define STUN_BINDREQ 0x0001
+#define STUN_BINDRESP 0x0101
+#define STUN_BINDERR 0x0111
+#define STUN_SECREQ 0x0002
+#define STUN_SECRESP 0x0102
+#define STUN_SECERR 0x0112
+
+#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
+
+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";
+}
+
+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";
+}
+
+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;
+}
+
+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;
+ }
+}
+
+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;
+ }
+}
+
+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));
+}
+
+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);
+}
+
+void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username)
+{
+ struct stun_header *req;
+ unsigned char reqdata[1024];
+ int reqlen, reqleft;
+ struct stun_attr *attr;
+
+ 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);
+ stun_send(rtp->s, suggestion, req);
+}
+
+static int stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *data, size_t len)
+{
+ struct stun_header *resp, *hdr = (struct stun_header *)data;
+ struct stun_attr *attr;
+ struct stun_state st;
+ int ret = STUN_IGNORE;
+ unsigned char respdata[1024];
+ int resplen, respleft;
+
+ if (len < sizeof(struct stun_header)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Runt STUN packet (only %zd, wanting at least %zd)\n", len, sizeof(struct stun_header));
+ return -1;
+ }
+ if (stundebug)
+ ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), ntohs(hdr->msglen));
+ if (ntohs(hdr->msglen) > len - sizeof(struct stun_header)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Scrambled STUN packet length (got %d, expecting %zd)\n", ntohs(hdr->msglen), len - sizeof(struct stun_header));
+ } else
+ len = ntohs(hdr->msglen);
+ data += sizeof(struct stun_header);
+ memset(&st, 0, sizeof(st));
+ while(len) {
+ if (len < sizeof(struct stun_attr)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Runt Attribute (got %zd, expecting %zd)\n", len, sizeof(struct stun_attr));
+ break;
+ }
+ attr = (struct stun_attr *)data;
+ if ((ntohs(attr->len) + sizeof(struct stun_attr)) > len) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", (int) (ntohs(attr->len) + sizeof(struct stun_attr)), (int) len);
+ break;
+ }
+ if (stun_process_attr(&st, attr)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr));
+ break;
+ }
+ /* Clear attribute in case previous entry was a string */
+ attr->attr = 0;
+ data += ntohs(attr->len) + sizeof(struct stun_attr);
+ len -= ntohs(attr->len) + sizeof(struct stun_attr);
+ }
+ /* Null terminate any string */
+ *data = '\0';
+ resp = (struct stun_header *)respdata;
+ resplen = 0;
+ respleft = sizeof(respdata) - sizeof(struct stun_header);
+ resp->id = hdr->id;
+ resp->msgtype = 0;
+ resp->msglen = 0;
+ attr = (struct stun_attr *)resp->ies;
+ if (!len) {
+ 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 List of current sessions */
+static AST_LIST_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 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) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Ignore potential DTMF echo from '%s'\n", ast_inet_ntoa(rtp->them.sin_addr));
+ rtp->resp = 0;
+ rtp->dtmfsamples = 0;
+ return &ast_null_frame;
+ }
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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;
+ event = ntohl(*((unsigned int *)(data)));
+ event &= 0x001F;
+ if (option_debug > 2 || rtpdebug)
+ ast_log(LOG_DEBUG, "Cisco DTMF Digit: %08x (len = %d)\n", event, len);
+ 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 && (rtp->resp != resp)) {
+ f = send_dtmf(rtp, AST_FRAME_DTMF_END);
+ }
+ rtp->resp = resp;
+ 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
+ * \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_log(LOG_DEBUG, "- 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_log(LOG_DEBUG, "- 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) {
+ ast_assert(errno != EBADF);
+ 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_log(LOG_DEBUG, "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));
+ }
+ }
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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) {
+ ast_log(LOG_WARNING, "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);
+ }
+ 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:
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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 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 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[bridged_payload].code)
+ return -1;
+
+
+ /* 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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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) {
+ ast_assert(errno != EBADF);
+ 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 ((stun_handle_packet(rtp->s, &sin, rtp->rawdata + AST_FRIENDLY_OFFSET, res) == 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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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 (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_MAX_AUDIO) ? AST_FRAME_VOICE : AST_FRAME_VIDEO;
+
+ 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_MAX_AUDIO) {
+ 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 / (ast_format_rate(rtp->f.subclass) / 1000);
+ } else {
+ /* 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;
+ if (mark)
+ rtp->f.subclass |= 0x1;
+
+ }
+ 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_G729A}, "audio", "G.729"},
+ {{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"},
+};
+
+/* 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 */
+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},
+ [103] = {1, AST_FORMAT_H263_PLUS},
+ [97] = {1, AST_FORMAT_ILBC},
+ [99] = {1, AST_FORMAT_H264},
+ [101] = {0, AST_RTP_DTMF},
+ [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;
+
+ ast_mutex_lock(&rtp->bridge_lock);
+
+ 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;
+
+ ast_mutex_unlock(&rtp->bridge_lock);
+}
+
+void ast_rtp_pt_default(struct ast_rtp* rtp)
+{
+ int i;
+
+ ast_mutex_lock(&rtp->bridge_lock);
+
+ /* 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;
+
+ ast_mutex_unlock(&rtp->bridge_lock);
+}
+
+void ast_rtp_pt_copy(struct ast_rtp *dest, struct ast_rtp *src)
+{
+ unsigned int i;
+
+ ast_mutex_lock(&dest->bridge_lock);
+ ast_mutex_lock(&src->bridge_lock);
+
+ 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;
+
+ ast_mutex_unlock(&src->bridge_lock);
+ ast_mutex_unlock(&dest->bridge_lock);
+}
+
+/*! \brief Get channel driver interface structure */
+static struct ast_rtp_protocol *get_proto(struct ast_channel *chan)
+{
+ struct ast_rtp_protocol *cur = NULL;
+
+ AST_LIST_LOCK(&protos);
+ AST_LIST_TRAVERSE(&protos, cur, list) {
+ if (cur->type == chan->tech->type)
+ break;
+ }
+ AST_LIST_UNLOCK(&protos);
+
+ return cur;
+}
+
+int ast_rtp_early_bridge(struct ast_channel *dest, struct ast_channel *src)
+{
+ struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */
+ struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video 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;
+ enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED;
+ int srccodec, destcodec, nat_active = 0;
+
+ /* Lock channels */
+ ast_channel_lock(dest);
+ if (src) {
+ while(ast_channel_trylock(src)) {
+ ast_channel_unlock(dest);
+ usleep(1);
+ ast_channel_lock(dest);
+ }
+ }
+
+ /* Find channel driver interfaces */
+ destpr = get_proto(dest);
+ if (src)
+ srcpr = get_proto(src);
+ if (!destpr) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Channel '%s' has no RTP, not doing anything\n", dest->name);
+ ast_channel_unlock(dest);
+ if (src)
+ ast_channel_unlock(src);
+ return 0;
+ }
+ if (!srcpr) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Channel '%s' has no RTP, not doing anything\n", src ? src->name : "<unspecified>");
+ ast_channel_unlock(dest);
+ if (src)
+ 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;
+ if (srcpr) {
+ 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;
+ }
+
+ /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
+ if (audio_dest_res != AST_RTP_TRY_NATIVE || (video_dest_res != AST_RTP_GET_FAILED && video_dest_res != AST_RTP_TRY_NATIVE)) {
+ /* Somebody doesn't want to play... */
+ ast_channel_unlock(dest);
+ if (src)
+ ast_channel_unlock(src);
+ return 0;
+ }
+ if (audio_src_res == AST_RTP_TRY_NATIVE && (video_src_res == AST_RTP_GET_FAILED || video_src_res == AST_RTP_TRY_NATIVE) && srcpr->get_codec)
+ srccodec = srcpr->get_codec(src);
+ else
+ srccodec = 0;
+ if (audio_dest_res == AST_RTP_TRY_NATIVE && (video_dest_res == AST_RTP_GET_FAILED || video_dest_res == AST_RTP_TRY_NATIVE) && destpr->get_codec)
+ destcodec = destpr->get_codec(dest);
+ else
+ destcodec = 0;
+ /* Ensure we have at least one matching codec */
+ if (srcp && !(srccodec & destcodec)) {
+ ast_channel_unlock(dest);
+ ast_channel_unlock(src);
+ 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 the client has NAT stuff turned on then just safe NAT is active */
+ if (srcp && (srcp->nat || ast_test_flag(srcp, FLAG_NAT_ACTIVE)))
+ nat_active = 1;
+ /* Bridge media early */
+ if (destpr->set_rtp_peer(dest, srcp, vsrcp, srccodec, nat_active))
+ ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", dest->name, src ? src->name : "<unspecified>");
+ ast_channel_unlock(dest);
+ if (src)
+ ast_channel_unlock(src);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Setting early bridge SDP of '%s' with that of '%s'\n", dest->name, src ? src->name : "<unspecified>");
+ return 1;
+}
+
+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_protocol *destpr = NULL, *srcpr = NULL;
+ enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_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;
+ 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))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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;
+ 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;
+
+ /* 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 || (video_dest_res != AST_RTP_GET_FAILED && video_dest_res != AST_RTP_TRY_NATIVE) || audio_src_res != AST_RTP_TRY_NATIVE || (video_src_res != AST_RTP_GET_FAILED && video_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 (media) {
+ /* Bridge early */
+ if (destpr->set_rtp_peer(dest, srcp, vsrcp, 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);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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 */
+
+ ast_mutex_lock(&rtp->bridge_lock);
+ rtp->current_RTP_PT[pt] = static_RTP_PT[pt];
+ ast_mutex_unlock(&rtp->bridge_lock);
+}
+
+/*! \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)
+{
+ if (pt < 0 || pt > MAX_RTP_PT)
+ return; /* bogus payload type */
+
+ ast_mutex_lock(&rtp->bridge_lock);
+ rtp->current_RTP_PT[pt].isAstFormat = 0;
+ rtp->current_RTP_PT[pt].code = 0;
+ ast_mutex_unlock(&rtp->bridge_lock);
+}
+
+/*! \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 */
+
+ ast_mutex_lock(&rtp->bridge_lock);
+
+ 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;
+ }
+ }
+
+ ast_mutex_unlock(&rtp->bridge_lock);
+
+ 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;
+
+ ast_mutex_lock(&rtp->bridge_lock);
+
+ *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;
+ }
+ }
+
+ ast_mutex_unlock(&rtp->bridge_lock);
+
+ 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 */
+ ast_mutex_lock(&rtp->bridge_lock);
+ result = rtp->current_RTP_PT[pt];
+ ast_mutex_unlock(&rtp->bridge_lock);
+
+ /* 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;
+
+ ast_mutex_lock(&rtp->bridge_lock);
+
+ 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;
+ ast_mutex_unlock(&rtp->bridge_lock);
+ 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;
+ ast_mutex_unlock(&rtp->bridge_lock);
+ 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;
+ ast_mutex_unlock(&rtp->bridge_lock);
+ return pt;
+ }
+ }
+
+ ast_mutex_unlock(&rtp->bridge_lock);
+
+ 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)
+ snprintf(start, size, "nothing)");
+ else if (size > 1)
+ *(end -1) = ')';
+
+ return buf;
+}
+
+static int rtp_socket(void)
+{
+ int s;
+ long flags;
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s > -1) {
+ 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->us.sin_family = AF_INET;
+ rtcp->them.sin_family = AF_INET;
+ rtcp->schedid = -1;
+
+ if (rtcp->s < 0) {
+ free(rtcp);
+ ast_log(LOG_WARNING, "Unable to allocate RTCP socket: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ return rtcp;
+}
+
+/*!
+ * \brief Initialize a new RTP structure.
+ *
+ */
+void ast_rtp_new_init(struct ast_rtp *rtp)
+{
+ ast_mutex_init(&rtp->bridge_lock);
+
+ 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 first;
+ int startplace;
+
+ if (!(rtp = ast_calloc(1, sizeof(*rtp))))
+ return NULL;
+
+ ast_rtp_new_init(rtp);
+
+ rtp->s = rtp_socket();
+ if (rtp->s < 0) {
+ free(rtp);
+ ast_log(LOG_ERROR, "Unable to allocate socket: %s\n", strerror(errno));
+ return NULL;
+ }
+ if (sched && rtcpenable) {
+ rtp->sched = sched;
+ rtp->rtcp = ast_rtcp_new();
+ }
+
+ /* Select a random port number in the range of possible RTP */
+ x = (rtpend == rtpstart) ? rtpstart : (ast_random() % (rtpend - rtpstart)) + rtpstart;
+ x = x & ~1;
+ /* Save it for future references. */
+ startplace = x;
+ /* Iterate tring to bind that port and incrementing it otherwise untill a port was found or no ports are available. */
+ for (;;) {
+ /* Must be an even port number by RTP spec */
+ rtp->us.sin_port = htons(x);
+ rtp->us.sin_addr = addr;
+ /* If there's rtcp, initialize it as well. */
+ if (rtp->rtcp) {
+ rtp->rtcp->us.sin_port = htons(x + 1);
+ rtp->rtcp->us.sin_addr = addr;
+ }
+ /* Try to bind it/them. */
+ if (!(first = bind(rtp->s, (struct sockaddr *)&rtp->us, sizeof(rtp->us))) &&
+ (!rtp->rtcp || !bind(rtp->rtcp->s, (struct sockaddr *)&rtp->rtcp->us, sizeof(rtp->rtcp->us))))
+ break;
+ if (!first) {
+ /* Primary bind succeeded! Gotta recreate it */
+ close(rtp->s);
+ rtp->s = rtp_socket();
+ }
+ if (errno != EADDRINUSE) {
+ /* We got an error that wasn't expected, abort! */
+ ast_log(LOG_ERROR, "Unexpected bind error: %s\n", strerror(errno));
+ close(rtp->s);
+ if (rtp->rtcp) {
+ close(rtp->rtcp->s);
+ free(rtp->rtcp);
+ }
+ free(rtp);
+ return NULL;
+ }
+ /* The port was used, increment it (by two). */
+ x += 2;
+ /* Did we go over the limit ? */
+ if (x > rtpend)
+ /* then, start from the begingig. */
+ x = (rtpstart + 1) & ~1;
+ /* Check if we reached the place were we started. */
+ if (x == startplace) {
+ /* If so, there's no ports available. */
+ ast_log(LOG_ERROR, "No RTP ports remaining. Can't setup media stream for this call.\n");
+ close(rtp->s);
+ if (rtp->rtcp) {
+ close(rtp->rtcp->s);
+ free(rtp->rtcp);
+ }
+ free(rtp);
+ return NULL;
+ }
+ }
+ 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;
+}
+
+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_settos(struct ast_rtp *rtp, int tos)
+{
+ int res;
+
+ if ((res = setsockopt(rtp->s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))))
+ ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
+ return res;
+}
+
+void ast_rtp_new_source(struct ast_rtp *rtp)
+{
+ if (rtp) {
+ rtp->set_marker_bit = 1;
+ }
+ return;
+}
+
+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;
+
+ ast_mutex_lock(&rtp->bridge_lock);
+ bridged = rtp->bridged;
+ ast_mutex_unlock(&rtp->bridge_lock);
+
+ return bridged;
+}
+
+void ast_rtp_stop(struct ast_rtp *rtp)
+{
+ if (rtp->rtcp) {
+ AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid);
+ }
+
+ 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->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);
+ }
+
+ 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) {
+ AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid);
+ close(rtp->rtcp->s);
+ free(rtp->rtcp);
+ rtp->rtcp=NULL;
+ }
+
+ ast_mutex_destroy(&rtp->bridge_lock);
+
+ 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;
+ rtp->lastdigitts = rtp->lastts + rtp->send_duration;
+
+ /* 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[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));
+
+ /* Send 3 termination packets */
+ for (i = 0; i < 3; i++) {
+ rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno));
+ res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
+ rtp->seqno++;
+ 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->lastts += rtp->send_duration;
+ rtp->sending_digit = 0;
+ rtp->send_digit = 0;
+
+ 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");
+ AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid);
+ 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));
+ AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid);
+ 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));
+ }
+ return res;
+}
+
+/*! \brief Send RTCP recepient'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");
+ AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid);
+ 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 */
+ AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid);
+ 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;
+}
+
+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;
+
+ if (rtp->sending_digit) {
+ return 0;
+ }
+
+ ms = calc_txstamp(rtp, &f->delivery);
+ /* Default prediction */
+ if (f->frametype == AST_FRAME_VOICE) {
+ 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 {
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms);
+ mark = 1;
+ }
+ }
+ } else if (f->frametype == AST_FRAME_VIDEO) {
+ 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 {
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "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;
+ }
+ }
+ }
+
+ /* If we have been explicitly told to set the marker bit do so */
+ if (rtp->set_marker_bit) {
+ mark = 1;
+ rtp->set_marker_bit = 0;
+ }
+
+ /* 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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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);
+
+ /* Do not schedule RR if RTCP isn't run */
+ if (rtp->rtcp && rtp->rtcp->them.sin_addr.s_addr && 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)) {
+ ast_log(LOG_WARNING, "RTP can only send voice and video\n");
+ return -1;
+ }
+
+ 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 */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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)) {
+ if (f->subclass == AST_FORMAT_G722) {
+ /* G.722 is silllllllllllllly */
+ f->samples /= 2;
+ }
+
+ 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);
+ } else {
+ f = _f;
+ }
+ if (f->data) {
+ if (f->subclass == AST_FORMAT_G722) {
+ /* G.722 is silllllllllllllly */
+ f->samples /= 2;
+ }
+ 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_LIST_LOCK(&protos);
+ AST_LIST_REMOVE(&protos, proto, list);
+ AST_LIST_UNLOCK(&protos);
+}
+
+/*! \brief Register interface to channel driver */
+int ast_rtp_proto_register(struct ast_rtp_protocol *proto)
+{
+ struct ast_rtp_protocol *cur;
+
+ AST_LIST_LOCK(&protos);
+ AST_LIST_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_LIST_UNLOCK(&protos);
+ return -1;
+ }
+ }
+ AST_LIST_INSERT_HEAD(&protos, proto, list);
+ AST_LIST_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_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,}, ac0 = {0,}, vac0 = {0,};
+ struct sockaddr_in t1 = {0,}, vt1 = {0,}, t0 = {0,}, vt0 = {0,};
+
+ /* Set it up so audio goes directly between the two endpoints */
+
+ /* Test the first channel */
+ if (!(pr0->set_rtp_peer(c0, p1, vp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))) {
+ ast_rtp_get_peer(p1, &ac1);
+ if (vp1)
+ ast_rtp_get_peer(vp1, &vac1);
+ } 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, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))) {
+ ast_rtp_get_peer(p0, &ac0);
+ if (vp0)
+ ast_rtp_get_peer(vp0, &vac0);
+ } 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);
+
+ /* 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_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
+ if (c0->tech_pvt == pvt0)
+ if (pr0->set_rtp_peer(c0, 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, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+ 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 (pr1->get_codec)
+ codec1 = pr1->get_codec(c1);
+ ast_rtp_get_peer(p0, &t0);
+ if (vp0)
+ ast_rtp_get_peer(vp0, &vt0);
+ if (pr0->get_codec)
+ codec0 = pr0->get_codec(c0);
+ if ((inaddrcmp(&t1, &ac1)) ||
+ (vp1 && inaddrcmp(&vt1, &vac1)) ||
+ (codec1 != oldcodec1)) {
+ if (option_debug > 1) {
+ ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n",
+ c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port), oldcodec1);
+ ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n",
+ c1->name, ast_inet_ntoa(vac1.sin_addr), ntohs(vac1.sin_port), oldcodec1);
+ }
+ if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : 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));
+ oldcodec1 = codec1;
+ }
+ if ((inaddrcmp(&t0, &ac0)) ||
+ (vp0 && inaddrcmp(&vt0, &vac0))) {
+ if (option_debug > 1) {
+ ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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, 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));
+ 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, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
+ if (pr1->set_rtp_peer(c1, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+ return AST_BRIDGE_RETRY;
+ }
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Oooh, got a %s\n", fr ? "digit" : "hangup");
+ if (c0->tech_pvt == pvt0)
+ if (pr0->set_rtp_peer(c0, 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, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+ 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) ||
+ (fr->subclass == AST_CONTROL_SRCUPDATE)) {
+ 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, 0, 0);
+ else
+ pr0->set_rtp_peer(c0, 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, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE));
+ else
+ pr0->set_rtp_peer(c0, p1, vp1, 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_log(LOG_DEBUG, "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 */
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+ }
+
+ if (pr0->set_rtp_peer(c0, 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, 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_log(LOG_DEBUG, "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 *fds, 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 and stash them away */
+ fds[0] = chan->fds[0];
+ chan->fds[0] = -1;
+
+ /* Now, fire up callback mode */
+ iod[0] = ast_io_add(rtp->io, fds[0], p2p_rtp_callback, AST_IO_IN, rtp);
+
+ return 1;
+}
+#else
+static int p2p_callback_enable(struct ast_channel *chan, struct ast_rtp *rtp, int *fds, 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 *fds, 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] = fds[0];
+ 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, rtp->s, 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)
+{
+ ast_mutex_lock(&rtp0->bridge_lock);
+ rtp0->bridged = rtp1;
+ ast_mutex_unlock(&rtp0->bridge_lock);
+
+ return;
+}
+
+/*! \brief Bridge loop for partial native bridge (packet2packet) */
+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_fds[2] = {-1, -1}, p1_fds[2] = {-1, -1};
+ 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_fds[0], &p0_iod[0]);
+ p1_callback = p2p_callback_enable(c1, p1, &p1_fds[0], &p1_iod[0]);
+
+ /* Now let go of the channel locks and be on our way */
+ ast_channel_unlock(c0);
+ ast_channel_unlock(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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
+ 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)
+ ast_log(LOG_NOTICE, "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;
+ /* Dependong 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;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Oooh, 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) ||
+ (fr->subclass == AST_CONTROL_SRCUPDATE)) {
+ /* 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_fds[0], &p0_iod[0]);
+ if (p1_callback)
+ p1_callback = p2p_callback_disable(c1, p1, &p1_fds[0], &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_fds[0], &p0_iod[0]);
+ p1_callback = p2p_callback_enable(c1, p1, &p1_fds[0], &p1_iod[0]);
+ }
+ ast_indicate_data(other, fr->subclass, fr->data, fr->datalen);
+ ast_frfree(fr);
+ } else {
+ *fo = fr;
+ *rc = who;
+ ast_log(LOG_DEBUG, "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 */
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+ }
+
+ /* If we are totally avoiding the core, then restore our link to it */
+ if (p0_callback)
+ p0_callback = p2p_callback_disable(c0, p0, &p0_fds[0], &p0_iod[0]);
+ if (p1_callback)
+ p1_callback = p2p_callback_disable(c1, p1, &p1_fds[0], &p1_iod[0]);
+
+ /* Break out of the direct bridge */
+ p2p_set_bridge(p0, NULL);
+ p2p_set_bridge(p1, NULL);
+
+ return res;
+}
+
+/*! \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_protocol *pr0 = NULL, *pr1 = NULL;
+ enum ast_rtp_get_result audio_p0_res = AST_RTP_GET_FAILED, video_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;
+ 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);
+ }
+
+ /* Ensure neither channel got hungup during lock avoidance */
+ if (ast_check_hangup(c0) || ast_check_hangup(c1)) {
+ ast_log(LOG_WARNING, "Got hangup while attempting to bridge '%s' and '%s'\n", c0->name, c1->name);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED;
+ }
+
+ /* 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;
+ 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;
+
+ /* 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 we need to feed frames into the core don't do a P2P bridge */
+ if ((audio_p0_res == AST_RTP_TRY_PARTIAL && ast_test_flag(p0, FLAG_P2P_NEED_DTMF)) ||
+ (audio_p1_res == AST_RTP_TRY_PARTIAL && ast_test_flag(p1, FLAG_P2P_NEED_DTMF))) {
+ 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 */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "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) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Cannot packet2packet bridge - packetization settings prevent it\n");
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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 {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s\n", c0->name, c1->name);
+ res = bridge_native_loop(c0, c1, p0, p1, vp0, vp1, pr0, pr1, codec0, codec1, timeoutms, flags, fo, rc, pvt0, pvt1);
+ }
+
+ return res;
+}
+
+static int rtp_do_debug_ip(int fd, int argc, char *argv[])
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int port = 0;
+ char *p, *arg;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ arg = argv[3];
+ p = strstr(arg, ":");
+ if (p) {
+ *p = '\0';
+ p++;
+ port = atoi(p);
+ }
+ hp = ast_gethostbyname(arg, &ahp);
+ if (hp == NULL)
+ return RESULT_SHOWUSAGE;
+ 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(fd, "RTP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtpdebugaddr.sin_addr));
+ else
+ ast_cli(fd, "RTP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtpdebugaddr.sin_addr), port);
+ rtpdebug = 1;
+ return RESULT_SUCCESS;
+}
+
+static int rtcp_do_debug_ip_deprecated(int fd, int argc, char *argv[])
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int port = 0;
+ char *p, *arg;
+ if (argc != 5)
+ return RESULT_SHOWUSAGE;
+
+ arg = argv[4];
+ p = strstr(arg, ":");
+ if (p) {
+ *p = '\0';
+ p++;
+ port = atoi(p);
+ }
+ hp = ast_gethostbyname(arg, &ahp);
+ if (hp == NULL)
+ return RESULT_SHOWUSAGE;
+ 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(fd, "RTCP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr));
+ else
+ ast_cli(fd, "RTCP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr), port);
+ rtcpdebug = 1;
+ return RESULT_SUCCESS;
+}
+
+static int rtcp_do_debug_ip(int fd, int argc, char *argv[])
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int port = 0;
+ char *p, *arg;
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ arg = argv[3];
+ p = strstr(arg, ":");
+ if (p) {
+ *p = '\0';
+ p++;
+ port = atoi(p);
+ }
+ hp = ast_gethostbyname(arg, &ahp);
+ if (hp == NULL)
+ return RESULT_SHOWUSAGE;
+ 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(fd, "RTCP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr));
+ else
+ ast_cli(fd, "RTCP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr), port);
+ rtcpdebug = 1;
+ return RESULT_SUCCESS;
+}
+
+static int rtp_do_debug(int fd, int argc, char *argv[])
+{
+ if (argc != 2) {
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ return rtp_do_debug_ip(fd, argc, argv);
+ }
+ rtpdebug = 1;
+ memset(&rtpdebugaddr,0,sizeof(rtpdebugaddr));
+ ast_cli(fd, "RTP Debugging Enabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int rtcp_do_debug_deprecated(int fd, int argc, char *argv[]) {
+ if (argc != 3) {
+ if (argc != 5)
+ return RESULT_SHOWUSAGE;
+ return rtcp_do_debug_ip_deprecated(fd, argc, argv);
+ }
+ rtcpdebug = 1;
+ memset(&rtcpdebugaddr,0,sizeof(rtcpdebugaddr));
+ ast_cli(fd, "RTCP Debugging Enabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int rtcp_do_debug(int fd, int argc, char *argv[]) {
+ if (argc != 2) {
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ return rtcp_do_debug_ip(fd, argc, argv);
+ }
+ rtcpdebug = 1;
+ memset(&rtcpdebugaddr,0,sizeof(rtcpdebugaddr));
+ ast_cli(fd, "RTCP Debugging Enabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int rtcp_do_stats_deprecated(int fd, int argc, char *argv[]) {
+ if (argc != 3) {
+ return RESULT_SHOWUSAGE;
+ }
+ rtcpstats = 1;
+ ast_cli(fd, "RTCP Stats Enabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int rtcp_do_stats(int fd, int argc, char *argv[]) {
+ if (argc != 2) {
+ return RESULT_SHOWUSAGE;
+ }
+ rtcpstats = 1;
+ ast_cli(fd, "RTCP Stats Enabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int rtp_no_debug(int fd, int argc, char *argv[])
+{
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ rtpdebug = 0;
+ ast_cli(fd,"RTP Debugging Disabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int rtcp_no_debug_deprecated(int fd, int argc, char *argv[])
+{
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ rtcpdebug = 0;
+ ast_cli(fd,"RTCP Debugging Disabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int rtcp_no_debug(int fd, int argc, char *argv[])
+{
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ rtcpdebug = 0;
+ ast_cli(fd,"RTCP Debugging Disabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int rtcp_no_stats_deprecated(int fd, int argc, char *argv[])
+{
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ rtcpstats = 0;
+ ast_cli(fd,"RTCP Stats Disabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int rtcp_no_stats(int fd, int argc, char *argv[])
+{
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ rtcpstats = 0;
+ ast_cli(fd,"RTCP Stats Disabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int stun_do_debug(int fd, int argc, char *argv[])
+{
+ if (argc != 2) {
+ return RESULT_SHOWUSAGE;
+ }
+ stundebug = 1;
+ ast_cli(fd, "STUN Debugging Enabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int stun_no_debug(int fd, int argc, char *argv[])
+{
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ stundebug = 0;
+ ast_cli(fd, "STUN Debugging Disabled\n");
+ return RESULT_SUCCESS;
+}
+
+static char debug_usage[] =
+ "Usage: rtp debug [ip host[:port]]\n"
+ " Enable dumping of all RTP packets to and from host.\n";
+
+static char no_debug_usage[] =
+ "Usage: rtp debug off\n"
+ " Disable all RTP debugging\n";
+
+static char stun_debug_usage[] =
+ "Usage: stun debug\n"
+ " Enable STUN (Simple Traversal of UDP through NATs) debugging\n";
+
+static char stun_no_debug_usage[] =
+ "Usage: stun debug off\n"
+ " Disable STUN debugging\n";
+
+static char rtcp_debug_usage[] =
+ "Usage: rtcp debug [ip host[:port]]\n"
+ " Enable dumping of all RTCP packets to and from host.\n";
+
+static char rtcp_no_debug_usage[] =
+ "Usage: rtcp debug off\n"
+ " Disable all RTCP debugging\n";
+
+static char rtcp_stats_usage[] =
+ "Usage: rtcp stats\n"
+ " Enable dumping of RTCP stats.\n";
+
+static char rtcp_no_stats_usage[] =
+ "Usage: rtcp stats off\n"
+ " Disable all RTCP stats\n";
+
+static struct ast_cli_entry cli_rtp_no_debug_deprecated = {
+ { "rtp", "no", "debug", NULL },
+ rtp_no_debug, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_rtp_rtcp_debug_ip_deprecated = {
+ { "rtp", "rtcp", "debug", "ip", NULL },
+ rtcp_do_debug_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_rtp_rtcp_debug_deprecated = {
+ { "rtp", "rtcp", "debug", NULL },
+ rtcp_do_debug_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_rtp_rtcp_no_debug_deprecated = {
+ { "rtp", "rtcp", "no", "debug", NULL },
+ rtcp_no_debug_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_rtp_rtcp_stats_deprecated = {
+ { "rtp", "rtcp", "stats", NULL },
+ rtcp_do_stats_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_rtp_rtcp_no_stats_deprecated = {
+ { "rtp", "rtcp", "no", "stats", NULL },
+ rtcp_no_stats_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_stun_no_debug_deprecated = {
+ { "stun", "no", "debug", NULL },
+ stun_no_debug, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_rtp[] = {
+ { { "rtp", "debug", "ip", NULL },
+ rtp_do_debug, "Enable RTP debugging on IP",
+ debug_usage },
+
+ { { "rtp", "debug", NULL },
+ rtp_do_debug, "Enable RTP debugging",
+ debug_usage },
+
+ { { "rtp", "debug", "off", NULL },
+ rtp_no_debug, "Disable RTP debugging",
+ no_debug_usage, NULL, &cli_rtp_no_debug_deprecated },
+
+ { { "rtcp", "debug", "ip", NULL },
+ rtcp_do_debug, "Enable RTCP debugging on IP",
+ rtcp_debug_usage, NULL, &cli_rtp_rtcp_debug_ip_deprecated },
+
+ { { "rtcp", "debug", NULL },
+ rtcp_do_debug, "Enable RTCP debugging",
+ rtcp_debug_usage, NULL, &cli_rtp_rtcp_debug_deprecated },
+
+ { { "rtcp", "debug", "off", NULL },
+ rtcp_no_debug, "Disable RTCP debugging",
+ rtcp_no_debug_usage, NULL, &cli_rtp_rtcp_no_debug_deprecated },
+
+ { { "rtcp", "stats", NULL },
+ rtcp_do_stats, "Enable RTCP stats",
+ rtcp_stats_usage, NULL, &cli_rtp_rtcp_stats_deprecated },
+
+ { { "rtcp", "stats", "off", NULL },
+ rtcp_no_stats, "Disable RTCP stats",
+ rtcp_no_stats_usage, NULL, &cli_rtp_rtcp_no_stats_deprecated },
+
+ { { "stun", "debug", NULL },
+ stun_do_debug, "Enable STUN debugging",
+ stun_debug_usage },
+
+ { { "stun", "debug", "off", NULL },
+ stun_no_debug, "Disable STUN debugging",
+ stun_no_debug_usage, NULL, &cli_stun_no_debug_deprecated },
+};
+
+int ast_rtp_reload(void)
+{
+ struct ast_config *cfg;
+ const char *s;
+
+ rtpstart = 5000;
+ rtpend = 31000;
+ dtmftimeout = DEFAULT_DTMF_TIMEOUT;
+ cfg = ast_config_load("rtp.conf");
+ 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;
+ }
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend);
+ return 0;
+}
+
+/*! \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();
+}
+
diff --git a/main/say.c b/main/say.c
new file mode 100644
index 000000000..5903b4597
--- /dev/null
+++ b/main/say.c
@@ -0,0 +1,7285 @@
+/*
+ * 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).
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <time.h>
+#include <ctype.h>
+#include <math.h>
+#include <stdio.h>
+
+#ifdef SOLARIS
+#include <iso/limits_iso.h>
+#endif
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.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[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'):
+ 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) {
+ 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
+
+ \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);
+
+/* 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);
+static int ast_say_enumeration_full_he(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_he(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_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_he(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_he(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 ast_say_datetime_from_now_he(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, "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, "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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ 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 -= ((num / 10) * 10);
+ } else {
+ if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num -= ((num / 100) * 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 = 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 = num % 1000000;
+ snprintf(fn, sizeof(fn), "digits/million");
+ } else {
+ ast_log(LOG_DEBUG, "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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ 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 -= ((num / 10) * 10);
+ } else if (num < 1000) {
+ hundered = num / 100;
+ if ( hundered == 1 ) {
+ snprintf(fn, sizeof(fn), "digits/1sto");
+ } else if ( hundered == 2 ) {
+ snprintf(fn, sizeof(fn), "digits/2ste");
+ } else {
+ res = ast_say_number_full_cz(chan,hundered,ints,language,options,audiofd,ctrlfd);
+ if (res)
+ return res;
+ if (hundered == 3 || hundered == 4) {
+ snprintf(fn, sizeof(fn), "digits/sta");
+ } else if ( hundered > 4 ) {
+ snprintf(fn, sizeof(fn), "digits/set");
+ }
+ }
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ playh = 0;
+ } else if (playa) {
+ snprintf(fn, sizeof(fn), "digits/and");
+ playa = 0;
+ } else if (num == 1 && cn == -1) {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ 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)
+ snprintf(fn, sizeof(fn), "digits/1N");
+ 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;
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ } 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)
+ snprintf(fn, sizeof(fn), "digits/million");
+ else
+ snprintf(fn, sizeof(fn), "digits/millions");
+ num = num % 1000000;
+ } else {
+ ast_log(LOG_DEBUG, "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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 100 && t) {
+ snprintf(fn, sizeof(fn), "digits/and");
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ num = 0;
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ num = num % 100;
+ if (hundreds == 1) {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", hundreds);
+ }
+ snprintf(fna, sizeof(fna), "digits/hundred");
+ t = 1;
+ } else if (num == 1000 && t == 0) {
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ num = 0;
+ } else if (num < 1000000) {
+ int thousands = num / 1000;
+ num = num % 1000;
+ t = 1;
+ if (thousands == 1) {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ snprintf(fna, sizeof(fna), "digits/thousand");
+ } else {
+ res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ }
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ num = num % 1000000;
+ t = 1;
+ if (millions == 1) {
+ snprintf(fn, sizeof(fn), "digits/1F");
+ snprintf(fna, sizeof(fna), "digits/million");
+ } else {
+ res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ snprintf(fn, sizeof(fn), "digits/millions");
+ }
+ } else if (num <= INT_MAX) {
+ int billions = num / 1000000000;
+ num = num % 1000000000;
+ t = 1;
+ if (billions == 1) {
+ snprintf(fn, sizeof(fn), "digits/1F");
+ snprintf(fna, sizeof(fna), "digits/milliard");
+ } else {
+ res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ snprintf(fn, sizeof(fn), "digits/milliards");
+ }
+ } else {
+ ast_log(LOG_DEBUG, "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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ playh = 0;
+ } else if (playa) {
+ snprintf(fn, sizeof(fn), "digits/and");
+ 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 -= ((num / 10) * 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;
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ num = 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;
+ snprintf(fn, sizeof(fn), "digits/million");
+ num = num % 1000000;
+ if (num && num < 100)
+ playa++;
+ } else {
+ ast_log(LOG_DEBUG, "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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playa) {
+ snprintf(fn, sizeof(fn), "digits/and");
+ 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 -= ((num/10)*10);
+ if (num)
+ playa++;
+ } else if (num == 100) {
+ snprintf(fn, sizeof(fn), "digits/100");
+ num = 0;
+ } else if (num < 200) {
+ snprintf(fn, sizeof(fn), "digits/100-and");
+ num -= 100;
+ } else {
+ if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
+ num -= ((num/100)*100);
+ } else if (num < 2000) {
+ num = num % 1000;
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ } else {
+ if (num < 1000000) {
+ res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num = num % 1000;
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ } 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;
+ snprintf(fn, sizeof(fn), "digits/million");
+ } else {
+ res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ snprintf(fn, sizeof(fn), "digits/millions");
+ }
+ num = num % 1000000;
+ } else {
+ ast_log(LOG_DEBUG, "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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ playh = 0;
+ } else if (playa) {
+ snprintf(fn, sizeof(fn), "digits/et");
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/60");
+ if ((num % 10) == 1) playa++;
+ num = num - 60;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/80");
+ num = num - 80;
+ } else if (num < 200) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ num = num - 100;
+ } else if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num = num % 100;
+ } else if (num < 2000) {
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ 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;
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ 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;
+ snprintf(fn, sizeof(fn), "digits/million");
+ num = num % 1000000;
+ } else {
+ ast_log(LOG_DEBUG, "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;
+}
+
+
+
+/* Hebrew syntax */
+/* Check doc/lang/hebrew-digits.txt for information about the various
+ * recordings required to make this translation work properly */
+#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 */
+ int tmpnum = 0;
+
+ char fn[SAY_NUM_BUF_SIZE] = "";
+
+ ast_verbose(VERBOSE_PREFIX_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, "m", 1)) {
+ mf = 1;
+ }
+ ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
+
+ /* 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_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d, tmpnum=%d\n", num, state, options, mf, tmpnum);
+
+ if (state == 1) {
+ state = 0;
+ } else if (state == 2) {
+ if ((num >= 11) && (num < 21)) {
+ if (mf < 0) {
+ snprintf(fn, sizeof(fn), "digits/ve");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/uu");
+ }
+ } else {
+ switch (num) {
+ case 1:
+ snprintf(fn, sizeof(fn), "digits/ve");
+ break;
+ case 2:
+ snprintf(fn, sizeof(fn), "digits/uu");
+ break;
+ case 3:
+ if (mf < 0) {
+ snprintf(fn, sizeof(fn), "digits/ve");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/uu");
+ }
+ break;
+ case 4:
+ snprintf(fn, sizeof(fn), "digits/ve");
+ break;
+ case 5:
+ snprintf(fn, sizeof(fn), "digits/ve");
+ break;
+ case 6:
+ snprintf(fn, sizeof(fn), "digits/ve");
+ break;
+ case 7:
+ snprintf(fn, sizeof(fn), "digits/ve");
+ break;
+ case 8:
+ snprintf(fn, sizeof(fn), "digits/uu");
+ break;
+ case 9:
+ snprintf(fn, sizeof(fn), "digits/ve");
+ break;
+ case 10:
+ snprintf(fn, sizeof(fn), "digits/ve");
+ break;
+ }
+ }
+ state = 0;
+ } else if (state == 3) {
+ snprintf(fn, sizeof(fn), "digits/1k");
+ state = 0;
+ } else if (num < 0) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ num = (-1) * num;
+ } else if (num < 20) {
+ if (mf < 0) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%dm", num);
+ }
+ num = 0;
+ } else if ((num < 100) && (num >= 20)) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
+ num = num % 10;
+ if (num > 0) {
+ state = 2;
+ }
+ } else if ((num >= 100) && (num < 1000)) {
+ tmpnum = num / 100;
+ snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
+ num = num - (tmpnum * 100);
+ if ((num > 0) && (num < 11)) {
+ state = 2;
+ }
+ } else if ((num >= 1000) && (num < 10000)) {
+ tmpnum = num / 1000;
+ snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
+ num = num - (tmpnum * 1000);
+ if ((num > 0) && (num < 11)) {
+ state = 2;
+ }
+ } else if (num < 20000) {
+ snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
+ num = num % 1000;
+ state = 3;
+ } else if (num < 1000000) {
+ res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ snprintf(fn, sizeof(fn), "digits/1k");
+ num = num % 1000;
+ if ((num > 0) && (num < 11)) {
+ state = 2;
+ }
+ } else if (num < 2000000) {
+ snprintf(fn, sizeof(fn), "digits/million");
+ num = num % 1000000;
+ if ((num > 0) && (num < 11)) {
+ state = 2;
+ }
+ } else if (num < 3000000) {
+ snprintf(fn, sizeof(fn), "digits/twomillion");
+ num = num - 2000000;
+ if ((num > 0) && (num < 11)) {
+ state = 2;
+ }
+ } else if (num < 1000000000) {
+ res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ snprintf(fn, sizeof(fn), "digits/million");
+ num = num % 1000000;
+ if ((num > 0) && (num < 11)) {
+ state = 2;
+ }
+ } else {
+ ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ tmpnum = 0;
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ 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 -= ((num / 10) * 10);
+ } else {
+ if (num < 1000) {
+ if ((num / 100) > 1) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ } else {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ }
+ num -= ((num / 100) * 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 = num % 1000;
+ if ((tempnum / 1000) < 2)
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
+ snprintf(fn, sizeof(fn), "digits/thousands");
+ } 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 = num % 1000000;
+ if ((tempnum / 1000000) < 2)
+ snprintf(fn, sizeof(fn), "digits/million");
+ else
+ snprintf(fn, sizeof(fn), "digits/millions");
+ } else {
+ ast_log(LOG_DEBUG, "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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ 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;
+ snprintf(fn, sizeof(fn), "digits/nl-en");
+ } 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 -= ((num / 100) * 100);
+ } else if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", num / 100);
+ playh++;
+ num -= ((num / 100) * 100);
+ } else {
+ if (num < 1100) {
+ /* thousand, not one-thousand */
+ num = 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 = 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 = num % 1000;
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ } 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 = num % 1000000;
+ snprintf(fn, sizeof(fn), "digits/million");
+ } else {
+ ast_log(LOG_DEBUG, "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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ playh = 0;
+ } else if (playa) {
+ snprintf(fn, sizeof(fn), "digits/and");
+ playa = 0;
+ } else if (num == 1 && cn == -1) {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ 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 -= ((num / 10) * 10);
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ if (hundreds == 1)
+ snprintf(fn, sizeof(fn), "digits/1N");
+ 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;
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ num = 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;
+ snprintf(fn, sizeof(fn), "digits/million");
+ num = num % 1000000;
+ if (num && num < 100)
+ playa++;
+ } else {
+ ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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 = (odmiana *) malloc(sizeof(odmiana));
+
+ 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 = (odmiana *) malloc(sizeof(odmiana));
+
+ 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 = (odmiana *) malloc(sizeof(odmiana));
+
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ 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)
+ snprintf(fn, sizeof(fn), "digits/100");
+ else if (num < 200)
+ snprintf(fn, sizeof(fn), "digits/100E");
+ 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;
+ }
+ snprintf(fn, sizeof(fn), "digits/1000");
+ 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)
+ snprintf(fn, sizeof(fn), "digits/1000000");
+ else
+ snprintf(fn, sizeof(fn), "digits/1000000S");
+
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ 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 -= ((num / 10) * 10);
+ } else if (num == 1 && cn == -1) { /* En eller ett? */
+ snprintf(fn, sizeof(fn), "digits/1N");
+ num = 0;
+ } else {
+ if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num -= ((num / 100) * 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 = num % 1000;
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ } 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 = num % 1000000;
+ snprintf(fn, sizeof(fn), "digits/million");
+ } else {
+ ast_log(LOG_DEBUG, "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;
+ int playt = 0;
+ int playz = 0;
+ int last_length = 0;
+ char buf[20] = "";
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
+
+ while (!res && (num || playh || playt || playz)) {
+ if (num < 0) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playz) {
+ snprintf(fn, sizeof(fn), "digits/0");
+ last_length = 0;
+ playz = 0;
+ } else if (playh) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ playh = 0;
+ } else if (playt) {
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ playt = 0;
+ } else if (num < 10) {
+ snprintf(buf, 10, "%d", num);
+ if (last_length - strlen(buf) > 1 && last_length != 0) {
+ last_length = strlen(buf);
+ playz++;
+ continue;
+ }
+ if (strcasecmp(language,"twz") == 0)
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(buf, 10, "%d", num);
+ if (last_length - strlen(buf) > 1 && last_length != 0) {
+ last_length = strlen(buf);
+ playz++;
+ continue;
+ }
+ last_length = strlen(buf);
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
+ num -= ((num / 10) * 10);
+ } else {
+ if (num < 1000){
+ snprintf(buf, 10, "%d", num);
+ if (last_length - strlen(buf) > 1 && last_length != 0) {
+ last_length = strlen(buf);
+ playz++;
+ continue;
+ }
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
+ playh++;
+ snprintf(buf, 10, "%d", num);
+ ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
+ last_length = strlen(buf);
+ num -= ((num / 100) * 100);
+ } else if (num < 10000){
+ snprintf(buf, 10, "%d", num);
+ if (last_length - strlen(buf) > 1 && last_length != 0 && last_length % strlen(buf) > 0) {
+ last_length = strlen(buf);
+ playz++;
+ continue;
+ }
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
+ playt++;
+ snprintf(buf, 10, "%d", num);
+ ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
+ last_length = strlen(buf);
+ num -= ((num / 1000) * 1000);
+ } else if (num < 100000000) { /* 100,000,000 */
+ res = ast_say_number_full_tw(chan, num / 10000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (((num / 10000) % (num/100000)) == 0)
+ playz++;
+
+ snprintf(buf, 10, "%d", num);
+ ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
+ num -= ((num / 10000) * 10000);
+ last_length = strlen(buf);
+ snprintf(fn, sizeof(fn), "digits/wan");
+ } else {
+ if (num < 1000000000) { /* 1000,000,000 */
+ res = ast_say_number_full_tw(chan, num / 100000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ snprintf(buf, 10, "%d", num);
+ ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
+ last_length = strlen(buf);
+ num -= ((num / 100000000) * 100000000);
+ snprintf(fn, sizeof(fn), "digits/yi");
+ } else {
+ ast_log(LOG_DEBUG, "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) {
+ snprintf(fn, sizeof(fn), "digits/minus");
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ } else if (lastdigits > 1 && lastdigits < 5) {
+ snprintf(fn, sizeof(fn), "digits/thousands-i");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/thousands");
+ }
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/million");
+ } else if (lastdigits > 1 && lastdigits < 5) {
+ snprintf(fn, sizeof(fn), "digits/million-a");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/millions");
+ }
+ num %= 1000000;
+ } else {
+ ast_log(LOG_DEBUG, "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: 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));
+ } else if (!strcasecmp(language, "he")) { /* Hebrew syntax */
+ return (ast_say_enumeration_full_he(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) {
+ snprintf(fn, sizeof(fn), "digits/minus"); /* 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) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-hundred");
+ }
+ } 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) {
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-thousand");
+ }
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/million");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-million");
+ }
+ } 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) {
+ snprintf(fn, sizeof(fn), "digits/billion");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-billion");
+ }
+ } else if (num == INT_MAX) {
+ snprintf(fn, sizeof(fn), "digits/h-last");
+ num = 0;
+ } else {
+ ast_log(LOG_DEBUG, "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) {
+ snprintf(fn, sizeof(fn), "digits/minus"); /* 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) {
+ snprintf(fn, sizeof(fn), "digits/and");
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", hundreds);
+ }
+ if (num) {
+ snprintf(fna, sizeof(fna), "digits/hundred");
+ } 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) {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ snprintf(fna, sizeof(fna), "digits/thousand");
+ } else {
+ if (t) {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ } 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) {
+ snprintf(fn, sizeof(fn), "digits/1F");
+ snprintf(fna, sizeof(fna), "digits/million");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/millions");
+ } 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) {
+ snprintf(fn, sizeof(fn), "digits/1F");
+ snprintf(fna, sizeof(fna), "digits/milliard");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ 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) {
+ snprintf(fn, sizeof(fna), "digits/milliards");
+ } 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_log(LOG_DEBUG, "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) {
+ snprintf(fn, sizeof(fn), "digits/minus"); /* 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) {
+ snprintf(fn, sizeof(fn), "digits/and");
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", hundreds);
+ }
+ if (num) {
+ snprintf(fna, sizeof(fna), "digits/hundred");
+ } 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) {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ snprintf(fna, sizeof(fna), "digits/thousand");
+ } else {
+ if (t) {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ } 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) {
+ snprintf(fn, sizeof(fn), "digits/1F");
+ snprintf(fna, sizeof(fna), "digits/million");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/millions");
+ } 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) {
+ snprintf(fn, sizeof(fn), "digits/1F");
+ snprintf(fna, sizeof(fna), "digits/milliard");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/1N");
+ 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) {
+ snprintf(fn, sizeof(fna), "digits/milliards");
+ } 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_log(LOG_DEBUG, "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 ast_say_enumeration_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ char fn[256] = "";
+ int mf = -1; /* +1 = Masculin; -1 = Feminin */
+ ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
+
+ if (options && !strncasecmp(options, "m", 1)) {
+ mf = -1;
+ }
+
+ ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num, options, mf);
+
+ while (!res && num) {
+ if (num < 0) {
+ snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
+ if (num > INT_MIN) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 21) {
+ if (mf < 0) {
+ if (num < 10) {
+ snprintf(fn, sizeof(fn), "digits/f-0%d", num);
+ } else {
+ snprintf(fn, sizeof(fn), "digits/f-%d", num);
+ }
+ } else {
+ if (num < 10) {
+ snprintf(fn, sizeof(fn), "digits/m-0%d", num);
+ } else {
+ snprintf(fn, sizeof(fn), "digits/m-%d", num);
+ }
+ }
+ num = 0;
+ } else if ((num < 100) && num >= 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
+ num = num % 10;
+ } else if ((num >= 100) && (num < 1000)) {
+ int tmpnum = num / 100;
+ snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
+ num = num - (tmpnum * 100);
+ } else if ((num >= 1000) && (num < 10000)) {
+ int tmpnum = num / 1000;
+ snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
+ num = num - (tmpnum * 1000);
+ } else if (num < 20000) {
+ snprintf(fn, sizeof(fn), "digits/m-%d", (num / 1000));
+ num = num % 1000;
+ } else if (num < 1000000) {
+ res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ snprintf(fn, sizeof(fn), "digits/1k");
+ num = num % 1000;
+ } else if (num < 2000000) {
+ snprintf(fn, sizeof(fn), "digits/1m");
+ num = num % 1000000;
+ } else if (num < 3000000) {
+ snprintf(fn, sizeof(fn), "digits/2m");
+ num = num - 2000000;
+ } else if (num < 1000000000) {
+ res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ snprintf(fn, sizeof(fn), "digits/1m");
+ num = num % 1000000;
+ } else {
+ ast_log(LOG_DEBUG, "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 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, "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, "ge") ) { /* Georgian syntax */
+ return(ast_say_date_ge(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
+ return (ast_say_date_he(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 tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&t,&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 tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&t,&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 tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&t,&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;
+}
+
+/* French syntax */
+int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&t,&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 tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&t,&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;
+}
+
+/* Portuguese syntax */
+int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct tm tm;
+ char fn[256];
+ int res = 0;
+
+ ast_localtime(&t, &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;
+}
+
+/* Hebrew syntax */
+int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&t, &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, "m");
+ }
+ if (!res) {
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "m");
+ }
+ 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, "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 tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "ABdY 'digits/at' IMp";
+
+ ast_localtime(&time,&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_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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/12");
+ 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
+ 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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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 tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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 tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (!format)
+ format = "A dBY HMS";
+
+ ast_localtime(&time,&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_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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/12");
+ 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
+ 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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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 tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (!format)
+ format = "A dBY HMS";
+
+ ast_localtime(&time,&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_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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/12");
+ 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
+ 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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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;
+}
+
+/* TODO: this probably is not the correct format for doxygen remarks */
+
+/** ast_say_date_with_format_he Say formmated 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 "HM" /* NOTE: In Hebrew we do not support 12 hours, only 24. No AM or PM exists in the Hebrew language */
+#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 tm tm;
+ int res = 0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (!format) {
+ format = IL_DATE_STR_FULL;
+ }
+
+ ast_localtime(&time, &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 '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 -> we do not support 12 hour based langauges in Hebrew */
+ case 'H':
+ case 'k': /* 24-Hour */
+ res = ast_say_number_full_he(chan, tm.tm_hour, ints, lang, "f", -1, -1);
+ break;
+ case 'M': /* Minute */
+ if (tm.tm_min >= 0 && tm.tm_min <= 9) /* say a leading zero if needed */
+ res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
+ res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM - There is no AM/PM in Hebrew... */
+ 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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+ char todo = format[offset]; /* The letter to format*/
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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 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(&time,&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';
+ 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/12");
+ 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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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 tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "AdBY 'digits/at' IMp";
+
+ ast_localtime(&time,&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':
+ /* 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/12");
+ 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
+ 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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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 tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "AdB 'digits/at' IMp";
+
+ ast_localtime(&time,&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':
+ /* 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/12");
+ 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
+ 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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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 tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "ABdY 'digits/at' IMp";
+
+ ast_localtime(&time,&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':
+ /* 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/12");
+ 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
+ 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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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 tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ ast_localtime(&thetime, &tm, timezone);
+
+ for (offset = 0 ; format[offset] != '\0' ; offset++) {
+ int remainder;
+ 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_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)
+ snprintf(nextmsg, sizeof(nextmsg), "digits/t-12");
+ 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)
+ snprintf(nextmsg, sizeof(nextmsg), "digits/p-m");
+ else
+ snprintf(nextmsg, sizeof(nextmsg), "digits/a-m");
+ res = wait_file(chan, ints, nextmsg, lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or AdBY */
+ {
+ time_t tv_sec = time(NULL);
+ struct tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&tv_sec,&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 = 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 */
+ {
+ time_t tv_sec = time(NULL);
+ struct tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&tv_sec, &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 = 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 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(&time,&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';
+ 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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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 < 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 tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "YBdAkM";
+
+ ast_localtime(&time,&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':
+ 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/12");
+ 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
+ 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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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, "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") || !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, "ge") ) { /* Georgian syntax */
+ return(ast_say_time_ge(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
+ return (ast_say_time_he(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 tm tm;
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&t, &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 tm tm;
+ int res = 0;
+
+ ast_localtime(&t, &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;
+}
+
+/* French syntax */
+int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct tm tm;
+ int res = 0;
+
+ ast_localtime(&t, &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 tm tm;
+ int res = 0;
+
+ ast_localtime(&t, &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 tm tm;
+ int res = 0;
+ int hour;
+
+ ast_localtime(&t, &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 tm tm;
+ int res = 0;
+
+ ast_localtime(&t, &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;
+}
+
+/* Taiwanese / Chinese syntax */
+int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct tm tm;
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&t, &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;
+}
+
+/* Hebrew syntax */
+int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct tm tm;
+ int res = 0;
+ int hour;
+
+ ast_localtime(&t, &tm, NULL);
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 12;
+
+ if (!res)
+ res = ast_say_number_full_he(chan, hour, ints, lang, "f", -1, -1);
+
+ if (tm.tm_min > 9) {
+ if (!res)
+ res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
+ } else if (tm.tm_min) {
+ if (!res) { /* say a leading zero if needed */
+ res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
+ } else {
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ 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, "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, "ge") ) { /* Georgian syntax */
+ return(ast_say_datetime_ge(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
+ return (ast_say_datetime_he(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 tm tm;
+ char fn[256];
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&t, &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 tm tm;
+ int res = 0;
+
+ ast_localtime(&t, &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 tm tm;
+ char fn[256];
+ int res = 0;
+
+ ast_localtime(&t, &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 tm tm;
+ int res = 0;
+
+ ast_localtime(&t, &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 tm tm;
+ char fn[256];
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&t, &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 tm tm;
+ int res = 0;
+
+ ast_localtime(&t, &tm, NULL);
+ res = ast_say_date(chan, t, ints, lang);
+ if (!res)
+ res = ast_say_time(chan, t, ints, lang);
+ return res;
+}
+
+/* Taiwanese / Chinese syntax */
+int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct tm tm;
+ char fn[256];
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&t, &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;
+}
+
+/* Hebrew syntax */
+int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct tm tm;
+ char fn[256];
+ int res = 0;
+ int hour;
+
+ ast_localtime(&t, &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, "f");
+ }
+
+ hour = tm.tm_hour;
+ if (!hour) {
+ hour = 12;
+ }
+
+ if (!res) {
+ res = ast_say_number(chan, hour, ints, lang, "f");
+ }
+
+ if (tm.tm_min > 9) {
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ }
+ } else if (tm.tm_min) {
+ if (!res) {
+ /* say a leading zero if needed */
+ res = ast_say_number(chan, 0, ints, lang, "f");
+ }
+ if (!res) {
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ }
+ } else {
+ 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, "f");
+ }
+ 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));
+ } else if (!strcasecmp(lang, "he")) { /* Georgian syntax */
+ return (ast_say_datetime_from_now_he(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;
+ time_t nowt;
+ int daydiff;
+ struct tm tm;
+ struct tm now;
+ char fn[256];
+
+ time(&nowt);
+
+ ast_localtime(&t, &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) {
+ 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;
+ time_t nowt;
+ int daydiff;
+ struct tm tm;
+ struct tm now;
+ char fn[256];
+
+ time(&nowt);
+
+ ast_localtime(&t, &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) {
+ 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;
+ time_t nowt;
+ int daydiff;
+ struct tm tm;
+ struct tm now;
+ char fn[256];
+
+ time(&nowt);
+
+ ast_localtime(&t, &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)
+ 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) {
+ snprintf(fn, sizeof(fn), "digits/pt-as");
+ } else {
+ snprintf(fn, sizeof(fn), "digits/pt-a");
+ }
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+ } else {
+ snprintf(fn, sizeof(fn), "digits/pt-ah");
+ 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;
+}
+
+/* Hebrew syntax */
+int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ int res = 0;
+ time_t nowt;
+ int daydiff;
+ struct tm tm;
+ struct tm now;
+ char fn[256];
+
+ time(&nowt);
+
+ ast_localtime(&t, &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) {
+ 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, "f");
+ }
+ } 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;
+}
+
+/*********************************** 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_log(LOG_DEBUG, "\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) {
+ snprintf(fn, sizeof(fn), "digits/0");
+ 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 -= ((num / 10) * 10);
+ } else if (num < 200) {
+ /* 100 < num < 200 */
+ snprintf(fn, sizeof(fn), "digits/hundred-100");
+ num -= ((num / 100) * 100);
+ } else if (num < 1000) {
+ /* 200 < num < 1000 */
+ snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
+ num -= ((num / 100) * 100);
+ } else if (num < 2000){
+ snprintf(fn, sizeof(fn), "digits/xilia");
+ num -= ((num / 1000) * 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 = 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 = num % 1000000;
+ snprintf(fn, sizeof(fn), "digits/millions");
+ } else {
+ ast_log(LOG_DEBUG, "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 tm tm;
+
+ char fn[256];
+ int res = 0;
+
+
+ ast_localtime(&t,&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 tm tm;
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&t, &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 tm tm;
+ char fn[256];
+ int res = 0;
+
+ ast_localtime(&t, &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 tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (!format)
+ format = "AdBY 'digits/at' IMp";
+
+ ast_localtime(&time,&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 '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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
+ 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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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;
+ struct tm tmnow;
+ time_t beg_today, tt;
+
+ gettimeofday(&now,NULL);
+ tt = now.tv_sec;
+ ast_localtime(&tt,&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 = tt - (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 */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/kai");
+ 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)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/seconds");
+ 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 = 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);
+
+ 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 = 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);
+
+ 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 tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&t,&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 tm tm;
+ int res = 0;
+
+ ast_localtime(&t, &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 tm tm;
+ int res = 0;
+
+ ast_localtime(&t, &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;
+ time_t nowt;
+ int daydiff;
+ struct tm tm;
+ struct tm now;
+ char fn[256];
+
+ time(&nowt);
+
+ ast_localtime(&t, &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/main/sched.c b/main/sched.c
new file mode 100644
index 000000000..cb6098635
--- /dev/null
+++ b/main/sched.c
@@ -0,0 +1,404 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk/sched.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/options.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)))
+ free(s);
+#endif
+
+ /* And the queue */
+ while ((s = AST_LIST_REMOVE_HEAD(&con->schedq, list)))
+ free(s);
+
+ /* And the context */
+ ast_mutex_unlock(&con->lock);
+ ast_mutex_destroy(&con->lock);
+ 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
+ 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_log(LOG_DEBUG, "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(&con->schedq, 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_log(LOG_DEBUG, "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) {
+ *tv = now;
+ }
+ return 0;
+}
+
+
+/*! \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_log(LOG_DEBUG, "ast_sched_add()\n"));
+
+ 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_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.
+ */
+#ifndef AST_DEVMODE
+int ast_sched_del(struct sched_context *con, int id)
+#else
+int _ast_sched_del(struct sched_context *con, int id, const char *file, int line, const char *function)
+#endif
+{
+ struct sched *s;
+
+ DEBUG(ast_log(LOG_DEBUG, "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(&con->schedq, 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) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Attempted to delete nonexistent schedule entry %d!\n", id);
+#ifndef AST_DEVMODE
+ ast_assert(s != NULL);
+#else
+ _ast_assert(0, "s != NULL", file, line, function);
+#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_log(LOG_DEBUG, "Asterisk Schedule Dump (%d in Q, %d Total, %d Cache)\n", con->schedcnt, con->eventcnt - 1, con->schedccnt);
+#else
+ ast_log(LOG_DEBUG, "Asterisk Schedule Dump (%d in Q, %d Total)\n", con->schedcnt, con->eventcnt - 1);
+#endif
+
+ ast_log(LOG_DEBUG, "=============================================================\n");
+ ast_log(LOG_DEBUG, "|ID Callback Data Time (sec:ms) |\n");
+ ast_log(LOG_DEBUG, "+-----+-----------------+-----------------+-----------------+\n");
+ AST_LIST_TRAVERSE(&con->schedq, q, list) {
+ struct timeval delta = ast_tvsub(q->when, tv);
+
+ ast_log(LOG_DEBUG, "|%.4d | %-15p | %-15p | %.6ld : %.6ld |\n",
+ q->id,
+ q->callback,
+ q->data,
+ delta.tv_sec,
+ (long int)delta.tv_usec);
+ }
+ ast_log(LOG_DEBUG, "=============================================================\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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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/main/sha1.c b/main/sha1.c
new file mode 100644
index 000000000..16ddd6aa3
--- /dev/null
+++ b/main/sha1.c
@@ -0,0 +1,385 @@
+/*
+ *
+ * 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/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 *);
+
+/*
+ * SHA1Reset
+ *
+ * Description:
+ * This function will initialize the SHA1Context in preparation
+ * for computing a new SHA1 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ *
+ * Returns:
+ * 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;
+}
+
+/*
+ * SHA1Result
+ *
+ * Description:
+ * 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.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA-1 hash.
+ * Message_Digest: [out]
+ * Where the digest is returned.
+ *
+ * Returns:
+ * 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;
+}
+
+/*
+ * SHA1Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update
+ * message_array: [in]
+ * An array of characters representing the next portion of
+ * the message.
+ * length: [in]
+ * The length of the message in message_array
+ *
+ * Returns:
+ * 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;
+}
+
+/*
+ * SHA1ProcessMessageBlock
+ *
+ * Description:
+ * This function will process the next 512 bits of the message
+ * stored in the Message_Block array.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the
+ * names used in the publication.
+ *
+ *
+ */
+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;
+}
+
+
+/*
+ * SHA1PadMessage
+ *
+ * Description:
+ * 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.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to pad
+ * ProcessMessageBlock: [in]
+ * The appropriate SHA*ProcessMessageBlock function
+ * 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/main/slinfactory.c b/main/slinfactory.c
new file mode 100644
index 000000000..bd5b8de4f
--- /dev/null
+++ b/main/slinfactory.c
@@ -0,0 +1,177 @@
+/*
+ * 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 <string.h>
+
+#include "asterisk/frame.h"
+#include "asterisk/slinfactory.h"
+#include "asterisk/logger.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)) == NULL) {
+ ast_log(LOG_WARNING, "Cannot build a path from %s to slin\n", ast_getformatname(f->subclass));
+ return 0;
+ } else {
+ sf->format = f->subclass;
+ }
+ }
+
+ if (!(begin_frame = ast_translate(sf->trans, f, 0)))
+ return 0;
+
+ duped_frame = ast_frdup(begin_frame);
+
+ ast_frfree(begin_frame);
+
+ if (!duped_frame)
+ return 0;
+ } else {
+ 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 (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 (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;
+ if (remain > (AST_SLINFACTORY_MAX_HOLD - sf->holdlen)) {
+ remain = AST_SLINFACTORY_MAX_HOLD - sf->holdlen;
+ }
+ 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/main/srv.c b/main/srv.c
new file mode 100644
index 000000000..cf99ef3ec
--- /dev/null
+++ b/main/srv.c
@@ -0,0 +1,246 @@
+/*
+ * 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 <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#if __APPLE_CC__ >= 1495
+#include <arpa/nameser_compat.h>
+#endif
+#include <resolv.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/logger.h"
+#include "asterisk/srv.h"
+#include "asterisk/dns.h"
+#include "asterisk/options.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(&c->entries, 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_REMOVE_CURRENT(&context->entries, list);
+ AST_LIST_INSERT_TAIL(&temp_list, current, 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_REMOVE_CURRENT(&temp_list, list);
+ AST_LIST_INSERT_TAIL(&newlist, current, 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);
+ if (option_verbose > 3) {
+ ast_verbose(VERBOSE_PREFIX_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/main/stdtime/Makefile b/main/stdtime/Makefile
new file mode 100644
index 000000000..d4b7f0093
--- /dev/null
+++ b/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 *.i
+
+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/main/stdtime/localtime.c b/main/stdtime/localtime.c
new file mode 100644
index 000000000..75968f787
--- /dev/null
+++ b/main/stdtime/localtime.c
@@ -0,0 +1,1651 @@
+/*
+ * 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"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+#include <float.h>
+
+
+#include "private.h"
+#include "tzfile.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#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";
+
+/*! \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 */
+
+#ifndef WRONG
+#define WRONG (-1)
+#endif /* !defined WRONG */
+
+/*!< \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 tm * gmtsub P((const time_t * timep, long offset,
+ struct tm * tmp));
+static struct tm * localsub P((const time_t * timep, long offset,
+ struct 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 time_t time1 P((struct tm * tmp,
+ struct tm * (*funcp) P((const time_t *,
+ long, struct tm *, const struct state *sp)),
+ long offset, const struct state *sp));
+static time_t time2 P((struct tm *tmp,
+ struct tm * (*funcp) P((const time_t *,
+ long, struct tm*, const struct state *sp)),
+ long offset, int * okayp, const struct state *sp));
+static time_t time2sub P((struct tm *tmp,
+ struct tm * (*funcp) (const time_t *,
+ long, struct tm*, const struct state *sp),
+ long offset, int * okayp, int do_norm_secs, const struct state *sp));
+static struct tm * timesub P((const time_t * timep, long offset,
+ const struct state * sp, struct tm * tmp));
+static int tmcomp P((const struct tm * atmp,
+ const struct 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 WRONG;
+ {
+ 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 WRONG;
+ if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
+ return WRONG;
+ (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 WRONG;
+ if ((fid = open(name, OPEN_MODE)) == -1)
+ return WRONG;
+ }
+ nread = read(fid, u.buf, sizeof u.buf);
+ if (close(fid) < 0 || nread <= 0)
+ return WRONG;
+ 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 WRONG;
+ 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 WRONG;
+ 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 WRONG;
+ }
+ 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 WRONG;
+ ttisp->tt_abbrind = (unsigned char) *p++;
+ if (ttisp->tt_abbrind < 0 ||
+ ttisp->tt_abbrind > sp->charcnt)
+ return WRONG;
+ }
+ 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 WRONG;
+ }
+ }
+ 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 WRONG;
+ }
+ }
+ /*
+ ** 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 WRONG;
+ stdlen = name - stdname;
+ name++;
+ } else {
+ name = getzname(name);
+ stdlen = name - stdname;
+ }
+ if (*name == '\0')
+ return WRONG;
+ name = getoffset(name, &stdoffset);
+ if (name == NULL)
+ return WRONG;
+ }
+ 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 WRONG;
+ 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 WRONG;
+ } 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 WRONG;
+ if (*name++ != ',')
+ return WRONG;
+ if ((name = getrule(name, &end)) == NULL)
+ return WRONG;
+ if (*name != '\0')
+ return WRONG;
+ 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 WRONG;
+ /*
+ ** 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 WRONG;
+ 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 WRONG;
+}
+
+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 tm *localsub(const time_t *timep, const long offset, struct tm *tmp, const struct state *sp)
+{
+ const struct ttinfo * ttisp;
+ int i;
+ struct tm * result;
+ const time_t t = *timep;
+
+ if (sp == NULL)
+ return gmtsub(timep, offset, tmp);
+ if ((sp->goback && t < sp->ats[0]) ||
+ (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+ time_t newt = t;
+ time_t seconds;
+ 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 NULL;
+ seconds = icycles;
+ seconds *= YEARSPERREPEAT;
+ seconds *= AVGSECSPERYEAR;
+ if (t < sp->ats[0])
+ newt += seconds;
+ else newt -= seconds;
+ if (newt < sp->ats[0] ||
+ newt > 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 < 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 < 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 < 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;
+#ifndef SOLARIS /* Solaris doesn't have this element */
+ tmp->tm_gmtoff = ttisp->tt_gmtoff;
+#endif
+#ifdef TM_ZONE
+ tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
+#endif /* defined TM_ZONE */
+ return result;
+}
+
+struct tm *ast_localtime(const time_t *timep, struct 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;
+}
+
+/*
+** gmtsub is to gmtime as localsub is to localtime.
+*/
+
+static struct tm *gmtsub(const time_t *timep, const long offset, struct tm *tmp)
+{
+ struct 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 tm *timesub(const time_t *timep, const long offset, const struct state *sp, struct 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 >= lp->ls_trans) {
+ if (*timep == 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 / SECSPERDAY;
+ rem = *timep - 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 */
+ 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 tm *atmp, const struct 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;
+ return result;
+}
+
+static time_t time2sub(struct tm *tmp, struct tm * (* const funcp) (const time_t *, long, struct 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;
+ time_t newt;
+ time_t t;
+ struct 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 = lo / 2 + hi / 2;
+ if (t < lo)
+ t = lo;
+ else if (t > hi)
+ t = hi;
+ if ((*funcp)(&t, offset, &mytm, sp) == NULL) {
+ /*
+ ** Assume that t is too extreme to be represented in
+ ** a struct tm; arrange things so that it is less
+ ** extreme on the next pass.
+ */
+ dir = (t > 0) ? 1 : -1;
+ } else dir = tmcomp(&mytm, &yourtm);
+ if (dir != 0) {
+ if (t == lo) {
+ ++t;
+ if (t <= lo)
+ return WRONG;
+ ++lo;
+ } else if (t == hi) {
+ --t;
+ if (t >= hi)
+ return WRONG;
+ --hi;
+ }
+ if (lo > hi)
+ return WRONG;
+ if (dir > 0)
+ hi = t;
+ else lo = t;
+ 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 = t + 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 = t + saved_seconds;
+ if ((newt < t) != (saved_seconds < 0))
+ return WRONG;
+ t = newt;
+ if ((*funcp)(&t, offset, tmp, sp))
+ *okayp = TRUE;
+ return t;
+}
+
+static time_t time2(struct tm *tmp, struct tm * (* const funcp) (const time_t*, long, struct tm*, const struct state *sp), const long offset, int *okayp, const struct state *sp)
+{
+ time_t 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 time_t time1(struct tm *tmp, struct tm * (* const funcp) (const time_t *, long, struct tm *, const struct state *), const long offset, const struct state *sp)
+{
+ time_t 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 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;
+}
+
+time_t ast_mktime(struct tm *tmp, const char *zone)
+{
+ const struct state *sp;
+ if (!(sp = ast_tzset(zone)))
+ return 0;
+ return time1(tmp, localsub, 0L, sp);
+}
+
diff --git a/main/stdtime/private.h b/main/stdtime/private.h
new file mode 100644
index 000000000..11793088d
--- /dev/null
+++ b/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/main/stdtime/test.c b/main/stdtime/test.c
new file mode 100644
index 000000000..9e8ce45da
--- /dev/null
+++ b/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/main/stdtime/tzfile.h b/main/stdtime/tzfile.h
new file mode 100644
index 000000000..9201b967f
--- /dev/null
+++ b/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/main/strcompat.c b/main/strcompat.c
new file mode 100644
index 000000000..243ef76cb
--- /dev/null
+++ b/main/strcompat.c
@@ -0,0 +1,472 @@
+/*
+ * 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 <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#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/main/tdd.c b/main/tdd.c
new file mode 100644
index 000000000..f1d7cf471
--- /dev/null
+++ b/main/tdd.c
@@ -0,0 +1,328 @@
+/*
+ * 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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include <ctype.h>
+
+#include "asterisk/ulaw.h"
+#include "asterisk/tdd.h"
+#include "asterisk/logger.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.spb = 176; /* 45.5 baud */
+ tdd->fskd.hdlc = 0; /* Async */
+ tdd->fskd.nbit = 5; /* 5 bits */
+ tdd->fskd.nstop = 1.5; /* 1.5 stop bits */
+ tdd->fskd.paridad = 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.pcola = 0; /* No clue */
+ tdd->fskd.cont = 0; /* Digital PLL reset */
+ tdd->fskd.x0 = 0.0;
+ tdd->fskd.state = 0;
+ tdd->pos = 0;
+ tdd->mode = 0;
+ tdd->charnum = 0;
+ } 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_serie(&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);
+
+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/main/term.c b/main/term.c
new file mode 100644
index 000000000..d051338d8
--- /dev/null
+++ b/main/term.c
@@ -0,0 +1,303 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "asterisk/term.h"
+#include "asterisk/options.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/main/threadstorage.c b/main/threadstorage.c
new file mode 100644
index 000000000..0bad38981
--- /dev/null
+++ b/main/threadstorage.c
@@ -0,0 +1,245 @@
+/*
+ * 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"
+
+#if defined(DEBUG_THREADLOCALS)
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "asterisk/logger.h"
+#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);
+
+/* 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 lock for the tls_objects list
+ *
+ * \note We can not use an ast_mutex_t for this. The reason is that this
+ * lock is used within the context of thread-local data destructors,
+ * and the ast_mutex_* API uses thread-local data. Allocating more
+ * thread-local data at that point just causes a memory leak.
+ */
+static pthread_mutex_t 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();
+
+ pthread_mutex_lock(&threadstoragelock);
+ AST_LIST_INSERT_TAIL(&tls_objects, to, entry);
+ pthread_mutex_unlock(&threadstoragelock);
+}
+
+void __ast_threadstorage_object_remove(void *key)
+{
+ struct tls_object *to;
+
+ pthread_mutex_lock(&threadstoragelock);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&tls_objects, to, entry) {
+ if (to->key == key) {
+ AST_LIST_REMOVE_CURRENT(&tls_objects, entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ pthread_mutex_unlock(&threadstoragelock);
+ if (to)
+ free(to);
+}
+
+void __ast_threadstorage_object_replace(void *key_old, void *key_new, size_t len)
+{
+ struct tls_object *to;
+
+ pthread_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;
+ pthread_mutex_unlock(&threadstoragelock);
+}
+
+static int handle_show_allocations(int fd, int argc, char *argv[])
+{
+ char *fn = NULL;
+ size_t len = 0;
+ unsigned int count = 0;
+ struct tls_object *to;
+
+ if (argc > 3)
+ fn = argv[3];
+
+ pthread_mutex_lock(&threadstoragelock);
+
+ AST_LIST_TRAVERSE(&tls_objects, to, entry) {
+ if (fn && strcasecmp(to->file, fn))
+ continue;
+
+ ast_cli(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++;
+ }
+
+ pthread_mutex_unlock(&threadstoragelock);
+
+ ast_cli(fd, "%10d bytes allocated in %d allocation%s\n", (int) len, count, count > 1 ? "s" : "");
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_show_summary(int fd, int argc, char *argv[])
+{
+ 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);
+
+ if (argc > 3)
+ fn = argv[3];
+
+ pthread_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++;
+ }
+
+ pthread_mutex_unlock(&threadstoragelock);
+
+ AST_LIST_TRAVERSE(&file_summary, file, entry) {
+ len += file->len;
+ count += file->count;
+ if (fn) {
+ ast_cli(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(fd, "%10d bytes in %d allocation%s in file %s\n",
+ (int) file->len, file->count, file->count > 1 ? "s" : "", file->name);
+ }
+ }
+
+ ast_cli(fd, "%10d bytes allocated in %d allocation%s\n", (int) len, count, count > 1 ? "s" : "");
+
+ return RESULT_SUCCESS;
+}
+
+static struct ast_cli_entry cli[] = {
+ {
+ .cmda = { "threadstorage", "show", "allocations", NULL },
+ .handler = handle_show_allocations,
+ .summary = "Display outstanding thread local storage allocations",
+ .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",
+ },
+ {
+ .cmda = { "threadstorage", "show", "summary", NULL },
+ .handler = handle_show_summary,
+ .summary = "Summarize outstanding memory allocations",
+ .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",
+ },
+};
+
+void threadstorage_init(void)
+{
+ pthread_mutex_init(&threadstoragelock, NULL);
+ ast_cli_register_multiple(cli, sizeof(cli) / sizeof(cli[0]));
+}
+
+#else /* !defined(DEBUG_THREADLOCALS) */
+
+void threadstorage_init(void)
+{
+}
+
+#endif /* !defined(DEBUG_THREADLOCALS) */
+
diff --git a/main/translate.c b/main/translate.c
new file mode 100644
index 000000000..598520c07
--- /dev/null
+++ b/main/translate.c
@@ -0,0 +1,986 @@
+/*
+ * 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/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/logger.h"
+#include "asterisk/translate.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+#include "asterisk/frame.h"
+#include "asterisk/sched.h"
+#include "asterisk/cli.h"
+#include "asterisk/term.h"
+
+#define MAX_RECALC 200 /* max sample recalc */
+
+/*! \brief the list of translators */
+static AST_LIST_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)) {
+ 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. */
+
+ /* Set the magic hint that this has been requested to be destroyed. */
+ pvt->datalen = -1;
+
+ return;
+ }
+
+ if (t->destroy)
+ t->destroy(pvt);
+ 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);
+
+ if (source == -1 || dest == -1) {
+ ast_log(LOG_WARNING, "No translator path: (%s codec is not valid)\n", source == -1 ? "starting" : "ending");
+ return NULL;
+ }
+
+ AST_LIST_LOCK(&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_LIST_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_LIST_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_LIST_UNLOCK(&translators);
+ return head;
+}
+
+/*! \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, ast_format_rate(f->subclass)));
+ }
+ delivery = f->delivery;
+ for ( ; out && p ; p = p->next) {
+ framein(p, out);
+ if (out != f)
+ ast_frfree(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, ast_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 timeval start;
+ int cost;
+ int out_rate = ast_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 = 99999;
+ 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 = 99999;
+ return;
+ }
+
+ start = ast_tvnow();
+
+ /* 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 = 99999;
+ return;
+ }
+ framein(pvt, f);
+ ast_frfree(f);
+ while ((f = t->frameout(pvt))) {
+ num_samples += f->samples;
+ ast_frfree(f);
+ }
+ }
+
+ cost = ast_tvdiff_ms(ast_tvnow(), start);
+
+ 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 */
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Resetting translation matrix\n");
+
+ bzero(tr_matrix, sizeof(tr_matrix));
+
+ /* first, compute all direct costs */
+ AST_LIST_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;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Discovered %d cost path from %s to %s, via %s\n", tr_matrix[x][z].cost,
+ ast_getformatname(1 << x), ast_getformatname(1 << z), ast_getformatname(1 << y));
+ changed++;
+ }
+ }
+ }
+ if (!changed)
+ break;
+ }
+}
+
+/*! \brief CLI "show translation" command handler */
+static int show_translation_deprecated(int fd, int argc, char *argv[])
+{
+#define SHOW_TRANS 13
+ int x, y, z;
+ int curlen = 0, longest = 0;
+
+ if (argc > 4)
+ return RESULT_SHOWUSAGE;
+
+ AST_LIST_LOCK(&translators);
+
+ if (argv[2] && !strcasecmp(argv[2], "recalc")) {
+ z = argv[3] ? atoi(argv[3]) : 1;
+
+ if (z <= 0) {
+ ast_cli(fd, " C'mon let's be serious here... defaulting to 1.\n");
+ z = 1;
+ }
+
+ if (z > MAX_RECALC) {
+ ast_cli(fd, " Maximum limit of recalc exceeded by %d, truncating value to %d\n", z - MAX_RECALC, MAX_RECALC);
+ z = MAX_RECALC;
+ }
+ ast_cli(fd, " Recalculating Codec Translation (number of sample seconds: %d)\n\n", z);
+ rebuild_matrix(z);
+ }
+
+ ast_cli(fd, " Translation times between formats (in milliseconds) for one second of data\n");
+ ast_cli(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++) {
+ char line[120];
+ char *buf = line;
+ size_t left = sizeof(line) - 1; /* one initial space */
+ /* next 2 lines run faster than using ast_build_string() */
+ *buf++ = ' ';
+ *buf = '\0';
+ for (y = -1; y < SHOW_TRANS; y++) {
+ if (y >= 0)
+ curlen = strlen(ast_getformatname(1 << (y)));
+
+ if (x >= 0 && y >= 0 && tr_matrix[x][y].step) {
+ /* XXX 999 is a little hackish
+ We don't want this number being larger than the shortest (or current) codec
+ For now, that is "gsm" */
+ ast_build_string(&buf, &left, "%*d", curlen + 1, tr_matrix[x][y].cost > 999 ? 0 : tr_matrix[x][y].cost);
+ } else if (x == -1 && y >= 0) {
+ /* Top row - use a dynamic size */
+ ast_build_string(&buf, &left, "%*s", curlen + 1, ast_getformatname(1 << (y)) );
+ } else if (y == -1 && x >= 0) {
+ /* Left column - use a static size. */
+ ast_build_string(&buf, &left, "%*s", longest, ast_getformatname(1 << (x)) );
+ } else if (x >= 0 && y >= 0) {
+ ast_build_string(&buf, &left, "%*s", curlen + 1, "-");
+ } else {
+ ast_build_string(&buf, &left, "%*s", longest, "");
+ }
+ }
+ ast_build_string(&buf, &left, "\n");
+ ast_cli(fd, "%s", line);
+ }
+ AST_LIST_UNLOCK(&translators);
+ return RESULT_SUCCESS;
+}
+
+static int show_translation(int fd, int argc, char *argv[])
+{
+ int x, y, z;
+ int curlen = 0, longest = 0;
+
+ if (argc > 5)
+ return RESULT_SHOWUSAGE;
+
+ AST_LIST_LOCK(&translators);
+
+ if (argv[3] && !strcasecmp(argv[3], "recalc")) {
+ z = argv[4] ? atoi(argv[4]) : 1;
+
+ if (z <= 0) {
+ ast_cli(fd, " C'mon let's be serious here... defaulting to 1.\n");
+ z = 1;
+ }
+
+ if (z > MAX_RECALC) {
+ ast_cli(fd, " Maximum limit of recalc exceeded by %d, truncating value to %d\n", z - MAX_RECALC, MAX_RECALC);
+ z = MAX_RECALC;
+ }
+ ast_cli(fd, " Recalculating Codec Translation (number of sample seconds: %d)\n\n", z);
+ rebuild_matrix(z);
+ }
+
+ ast_cli(fd, " Translation times between formats (in milliseconds) for one second of data\n");
+ ast_cli(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++) {
+ char line[120];
+ char *buf = line;
+ size_t left = sizeof(line) - 1; /* one initial space */
+ /* next 2 lines run faster than using ast_build_string() */
+ *buf++ = ' ';
+ *buf = '\0';
+ for (y = -1; y < SHOW_TRANS; y++) {
+ if (y >= 0)
+ curlen = strlen(ast_getformatname(1 << (y)));
+
+ if (x >= 0 && y >= 0 && tr_matrix[x][y].step) {
+ /* XXX 999 is a little hackish
+ We don't want this number being larger than the shortest (or current) codec
+ For now, that is "gsm" */
+ ast_build_string(&buf, &left, "%*d", curlen + 1, tr_matrix[x][y].cost > 999 ? 0 : tr_matrix[x][y].cost);
+ } else if (x == -1 && y >= 0) {
+ /* Top row - use a dynamic size */
+ ast_build_string(&buf, &left, "%*s", curlen + 1, ast_getformatname(1 << (y)) );
+ } else if (y == -1 && x >= 0) {
+ /* Left column - use a static size. */
+ ast_build_string(&buf, &left, "%*s", longest, ast_getformatname(1 << (x)) );
+ } else if (x >= 0 && y >= 0) {
+ ast_build_string(&buf, &left, "%*s", curlen + 1, "-");
+ } else {
+ ast_build_string(&buf, &left, "%*s", longest, "");
+ }
+ }
+ ast_build_string(&buf, &left, "\n");
+ ast_cli(fd, "%s", line);
+ }
+ AST_LIST_UNLOCK(&translators);
+ return RESULT_SUCCESS;
+}
+
+static char show_trans_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";
+
+static struct ast_cli_entry cli_show_translation_deprecated = {
+ { "show", "translation", NULL },
+ show_translation_deprecated, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_translate[] = {
+ { { "core", "show", "translation", NULL },
+ show_translation, "Display translation matrix",
+ show_trans_usage, NULL, &cli_show_translation_deprecated },
+};
+
+/*! \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;
+
+ 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->srcfmt == -1 || t->dstfmt == -1) {
+ ast_log(LOG_WARNING, "Invalid translator path: (%s codec is not valid)\n", t->srcfmt == -1 ? "starting" : "ending");
+ return -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);
+
+ if (option_verbose > 1) {
+ char tmp[80];
+
+ ast_verbose(VERBOSE_PREFIX_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_LIST_LOCK(&translators);
+
+ /* find any existing translators that provide this same srcfmt/dstfmt,
+ and put this one in order based on cost */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) {
+ if ((u->srcfmt == t->srcfmt) &&
+ (u->dstfmt == t->dstfmt) &&
+ (u->cost > t->cost)) {
+ AST_LIST_INSERT_BEFORE_CURRENT(&translators, t, list);
+ t = NULL;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ /* if no existing translator was found for this format combination,
+ add it to the beginning of the list */
+ if (t)
+ AST_LIST_INSERT_HEAD(&translators, t, list);
+
+ rebuild_matrix(0);
+
+ AST_LIST_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_LIST_LOCK(&translators);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) {
+ if (u == t) {
+ AST_LIST_REMOVE_CURRENT(&translators, list);
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_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_LIST_TRAVERSE_SAFE_END;
+
+ if (found)
+ rebuild_matrix(0);
+
+ AST_LIST_UNLOCK(&translators);
+
+ return (u ? 0 : -1);
+}
+
+void ast_translator_activate(struct ast_translator *t)
+{
+ AST_LIST_LOCK(&translators);
+ t->active = 1;
+ rebuild_matrix(0);
+ AST_LIST_UNLOCK(&translators);
+}
+
+void ast_translator_deactivate(struct ast_translator *t)
+{
+ AST_LIST_LOCK(&translators);
+ t->active = 0;
+ rebuild_matrix(0);
+ AST_LIST_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_LIST_LOCK(&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_LIST_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);
+
+ if (src == -1 || dest == -1) {
+ ast_log(LOG_WARNING, "No translator path: (%s codec is not valid)\n", src == -1 ? "starting" : "ending");
+ return -1;
+ }
+ AST_LIST_LOCK(&translators);
+
+ if (tr_matrix[src][dest].step)
+ res = tr_matrix[src][dest].multistep + 1;
+
+ AST_LIST_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_LIST_LOCK(&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_MAX_AUDIO; 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_MAX_VIDEO; 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_LIST_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->datalen != -1)
+ return;
+
+ destroy(pvt);
+}
diff --git a/main/udptl.c b/main/udptl.c
new file mode 100644
index 000000000..679282731
--- /dev/null
+++ b/main/udptl.c
@@ -0,0 +1,1247 @@
+/*
+ * 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>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+
+#include "asterisk/udptl.h"
+#include "asterisk/frame.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/acl.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+#include "asterisk/unaligned.h"
+#include "asterisk/utils.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;
+
+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 struct ast_udptl_protocol *protos;
+
+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 (*len >= limit)
+ return -1;
+ if ((buf[*len] & 0x80) == 0) {
+ *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;
+ }
+ *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));
+ ast_assert(errno != EBADF);
+ 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_log(LOG_DEBUG, "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_verbose("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) {
+ 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);
+ free(udptl);
+ return NULL;
+ }
+ if (++x > udptlend)
+ x = udptlstart;
+ if (x == startplace) {
+ ast_log(LOG_WARNING, "No UDPTL ports remaining\n");
+ close(udptl->fd);
+ 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_settos(struct ast_udptl *udptl, int tos)
+{
+ int res;
+
+ if ((res = setsockopt(udptl->fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))))
+ ast_log(LOG_WARNING, "UDPTL unable to set TOS to %d\n", tos);
+ return res;
+}
+
+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)
+{
+ memset(them, 0, sizeof(*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);
+ 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_verbose("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)
+{
+ struct ast_udptl_protocol *cur;
+ struct ast_udptl_protocol *prev;
+
+ cur = protos;
+ prev = NULL;
+ while (cur) {
+ if (cur == proto) {
+ if (prev)
+ prev->next = proto->next;
+ else
+ protos = proto->next;
+ return;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+}
+
+int ast_udptl_proto_register(struct ast_udptl_protocol *proto)
+{
+ struct ast_udptl_protocol *cur;
+
+ cur = protos;
+ while (cur) {
+ if (cur->type == proto->type) {
+ ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type);
+ return -1;
+ }
+ cur = cur->next;
+ }
+ proto->next = protos;
+ protos = proto;
+ return 0;
+}
+
+static struct ast_udptl_protocol *get_proto(struct ast_channel *chan)
+{
+ struct ast_udptl_protocol *cur;
+
+ cur = protos;
+ while (cur) {
+ if (cur->type == chan->tech->type)
+ return cur;
+ cur = cur->next;
+ }
+ return NULL;
+}
+
+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);
+ memset(&ac1, 0, sizeof(ac1));
+ } 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);
+ memset(&ac0, 0, sizeof(ac0));
+ } 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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d\n",
+ c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port));
+ ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d\n",
+ c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port));
+ ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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 int udptl_do_debug_ip(int fd, int argc, char *argv[])
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int port;
+ char *p;
+ char *arg;
+
+ port = 0;
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ arg = argv[3];
+ p = strstr(arg, ":");
+ if (p) {
+ *p = '\0';
+ p++;
+ port = atoi(p);
+ }
+ hp = ast_gethostbyname(arg, &ahp);
+ if (hp == NULL)
+ return RESULT_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(fd, "UDPTL Debugging Enabled for IP: %s\n", ast_inet_ntoa(udptldebugaddr.sin_addr));
+ else
+ ast_cli(fd, "UDPTL Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(udptldebugaddr.sin_addr), port);
+ udptldebug = 1;
+ return RESULT_SUCCESS;
+}
+
+static int udptl_do_debug(int fd, int argc, char *argv[])
+{
+ if (argc != 2) {
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ return udptl_do_debug_ip(fd, argc, argv);
+ }
+ udptldebug = 1;
+ memset(&udptldebugaddr,0,sizeof(udptldebugaddr));
+ ast_cli(fd, "UDPTL Debugging Enabled\n");
+ return RESULT_SUCCESS;
+}
+
+static int udptl_nodebug(int fd, int argc, char *argv[])
+{
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ udptldebug = 0;
+ ast_cli(fd,"UDPTL Debugging Disabled\n");
+ return RESULT_SUCCESS;
+}
+
+static char debug_usage[] =
+ "Usage: udptl debug [ip host[:port]]\n"
+ " Enable dumping of all UDPTL packets to and from host.\n";
+
+static char nodebug_usage[] =
+ "Usage: udptl debug off\n"
+ " Disable all UDPTL debugging\n";
+
+static struct ast_cli_entry cli_udptl_no_debug = {
+ { "udptl", "no", "debug", NULL },
+ udptl_nodebug, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_udptl[] = {
+ { { "udptl", "debug", NULL },
+ udptl_do_debug, "Enable UDPTL debugging",
+ debug_usage },
+
+ { { "udptl", "debug", "ip", NULL },
+ udptl_do_debug, "Enable UDPTL debugging on IP",
+ debug_usage },
+
+ { { "udptl", "debug", "off", NULL },
+ udptl_nodebug, "Disable UDPTL debugging",
+ nodebug_usage, NULL, &cli_udptl_no_debug },
+};
+
+void ast_udptl_reload(void)
+{
+ struct ast_config *cfg;
+ const char *s;
+
+ udptlstart = 4500;
+ udptlend = 4999;
+ udptlfectype = 0;
+ udptlfecentries = 0;
+ udptlfecspan = 0;
+ udptlmaxdatagram = 0;
+
+ if ((cfg = ast_config_load("udptl.conf"))) {
+ 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;
+ }
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "UDPTL allocating from port range %d -> %d\n", udptlstart, udptlend);
+}
+
+void ast_udptl_init(void)
+{
+ ast_cli_register_multiple(cli_udptl, sizeof(cli_udptl) / sizeof(struct ast_cli_entry));
+ ast_udptl_reload();
+}
diff --git a/main/ulaw.c b/main/ulaw.c
new file mode 100644
index 000000000..2735f6cce
--- /dev/null
+++ b/main/ulaw.c
@@ -0,0 +1,106 @@
+/*
+ * 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"
+
+#define ZEROTRAP /*!< turn on the trap as per the MIL-STD */
+#define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+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;
+}
+
+/*!
+ * \brief Set up mu-law conversion table
+ */
+void ast_ulaw_init(void)
+{
+ int i;
+ 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);
+ }
+}
+
diff --git a/main/utils.c b/main/utils.c
new file mode 100644
index 000000000..d7863198b
--- /dev/null
+++ b/main/utils.c
@@ -0,0 +1,1414 @@
+/*
+ * 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 <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.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/logger.h"
+#include "asterisk/md5.h"
+#include "asterisk/sha1.h"
+#include "asterisk/options.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, inet_ntoa_buf_init);
+
+#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;
+}
+
+/*! \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 (((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);
+}
+
+#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;
+ int i;
+
+ pthread_mutex_lock(&lock_infos_lock.mutex);
+ AST_LIST_REMOVE(&lock_infos, lock_info, entry);
+ pthread_mutex_unlock(&lock_infos_lock.mutex);
+
+
+ for (i = 0; i < lock_info->num_locks; i++) {
+ if (lock_info->locks[i].pending == -1) {
+ /* This just means that the last lock this thread went for was by
+ * using trylock, and it failed. This is fine. */
+ break;
+ }
+
+ ast_log(LOG_ERROR,
+ "Thread '%s' still has a lock! - '%s' (%p) from '%s' in %s:%d!\n",
+ lock_info->thread_name,
+ lock_info->locks[i].lock_name,
+ lock_info->locks[i].lock_addr,
+ lock_info->locks[i].func,
+ lock_info->locks[i].file,
+ lock_info->locks[i].line_num
+ );
+ }
+
+ pthread_mutex_destroy(&lock_info->lock);
+ 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, thread_lock_info_init, 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;
+ }
+
+ if (i && lock_info->locks[i - 1].pending == -1) {
+ /* The last lock on the list was one that this thread tried to lock but
+ * failed at doing so. It has now moved on to something else, so remove
+ * the old lock from the list. */
+ i--;
+ lock_info->num_locks--;
+ memset(&lock_info->locks[i], 0, sizeof(lock_info->locks[0]));
+ }
+
+ 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 *lock_addr)
+{
+ 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);
+ if (lock_info->locks[lock_info->num_locks - 1].lock_addr == lock_addr) {
+ lock_info->locks[lock_info->num_locks - 1].pending = 0;
+ }
+ pthread_mutex_unlock(&lock_info->lock);
+}
+
+void ast_mark_lock_failed(void *lock_addr)
+{
+ 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);
+ if (lock_info->locks[lock_info->num_locks - 1].lock_addr == lock_addr) {
+ 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);
+}
+
+int ast_find_lock_info(void *lock_addr, char *filename, size_t filename_size, int *lineno, char *func, size_t func_size, char *mutex_name, size_t mutex_name_size)
+{
+ struct thr_lock_info *lock_info;
+ int i = 0;
+
+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
+ return -1;
+
+ 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 -1;
+ }
+
+ ast_copy_string(filename, lock_info->locks[i].file, filename_size);
+ *lineno = lock_info->locks[i].line_num;
+ ast_copy_string(func, lock_info->locks[i].func, func_size);
+ ast_copy_string(mutex_name, lock_info->locks[i].lock_name, mutex_name_size);
+
+ pthread_mutex_unlock(&lock_info->lock);
+
+ return 0;
+}
+
+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 int handle_show_locks(int fd, int argc, char *argv[])
+{
+ struct thr_lock_info *lock_info;
+ struct ast_dynamic_str *str;
+
+ if (!(str = ast_dynamic_str_create(4096)))
+ return RESULT_FAILURE;
+
+ ast_dynamic_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 RESULT_FAILURE;
+
+ pthread_mutex_lock(&lock_infos_lock.mutex);
+ AST_LIST_TRAVERSE(&lock_infos, lock_info, entry) {
+ int i;
+ if (lock_info->num_locks) {
+ ast_dynamic_str_append(&str, 0, "=== Thread ID: %u (%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_dynamic_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_dynamic_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_dynamic_str_append(&str, 0, "=== -------------------------------------------------------------------\n"
+ "===\n");
+ if (!str)
+ break;
+ }
+ }
+ pthread_mutex_unlock(&lock_infos_lock.mutex);
+
+ if (!str)
+ return RESULT_FAILURE;
+
+ ast_dynamic_str_append(&str, 0, "=======================================================================\n"
+ "\n");
+
+ if (!str)
+ return RESULT_FAILURE;
+
+ ast_cli(fd, "%s", str->str);
+
+ free(str);
+
+ return RESULT_SUCCESS;
+}
+
+static char show_locks_help[] =
+"Usage: core show locks\n"
+" This command is for lock debugging. It prints out which locks\n"
+"are owned by each active thread.\n";
+
+static struct ast_cli_entry utils_cli[] = {
+ { { "core", "show", "locks", NULL }, handle_show_locks,
+ "Show which locks are locked by which thread", show_locks_help },
+};
+
+#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
+ */
+ 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;
+ if (asprintf(&a->name, "%-20s started at [%5d] %s %s()",
+ start_fn, line, file, caller) < 0) {
+ ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
+ a->name = NULL;
+ }
+ 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_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);
+}
+
+int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
+{
+ struct timeval start = ast_tvnow();
+ int res = 0;
+ int elapsed = 0;
+
+ while (len) {
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = POLLOUT,
+ };
+
+ /* poll() until the fd is writable without blocking */
+ while ((res = poll(&pfd, 1, timeoutms - elapsed)) <= 0) {
+ if (res == 0) {
+ /* timed out. */
+ ast_log(LOG_NOTICE, "Timed out trying to write\n");
+ return -1;
+ } else if (res == -1) {
+ /* poll() returned an error, check to see if it was fatal */
+
+ if (errno == EINTR || errno == EAGAIN) {
+ elapsed = ast_tvdiff_ms(ast_tvnow(), start);
+ if (elapsed > timeoutms) {
+ /* We've taken too long to write
+ * This is only an error condition if we haven't finished writing. */
+ res = len ? -1 : 0;
+ break;
+ }
+ /* This was an acceptable error, go back into poll() */
+ continue;
+ }
+
+ /* Fatal error, bail. */
+ ast_log(LOG_ERROR, "poll returned error: %s\n", strerror(errno));
+
+ return -1;
+ }
+ }
+
+ res = write(fd, s, len);
+
+ if (res < 0 && errno != EAGAIN && errno != EINTR) {
+ /* fatal error from write() */
+ ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (res < 0) {
+ /* It was an acceptable error */
+ res = 0;
+ }
+
+ /* Update how much data we have left to write */
+ len -= res;
+ s += res;
+ res = 0;
+
+ elapsed = ast_tvdiff_ms(ast_tvnow(), start);
+ if (elapsed > timeoutms) {
+ /* We've taken too long to write
+ * This is only an error condition if we haven't finished writing. */
+ res = len ? -1 : 0;
+ break;
+ }
+ }
+
+ 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;
+}
+
+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);
+
+long int ast_random(void)
+{
+ long int res;
+ ast_mutex_lock(&randomlock);
+ res = random();
+ ast_mutex_unlock(&randomlock);
+ return res;
+}
+#endif
+
+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';
+}
+
+const char __ast_string_field_empty[] = "";
+
+static int add_string_pool(struct ast_string_field_mgr *mgr, size_t size)
+{
+ struct ast_string_field_pool *pool;
+
+ if (!(pool = ast_calloc(1, sizeof(*pool) + size)))
+ return -1;
+
+ pool->prev = mgr->pool;
+ mgr->pool = pool;
+ mgr->size = size;
+ mgr->space = size;
+ mgr->used = 0;
+
+ return 0;
+}
+
+int __ast_string_field_init(struct ast_string_field_mgr *mgr, size_t size,
+ ast_string_field *fields, int num_fields)
+{
+ int index;
+
+ if (add_string_pool(mgr, size))
+ return -1;
+
+ for (index = 0; index < num_fields; index++)
+ fields[index] = __ast_string_field_empty;
+
+ return 0;
+}
+
+ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr, size_t needed,
+ ast_string_field *fields, int num_fields)
+{
+ char *result = NULL;
+
+ if (__builtin_expect(needed > mgr->space, 0)) {
+ size_t new_size = mgr->size * 2;
+
+ while (new_size < needed)
+ new_size *= 2;
+
+ if (add_string_pool(mgr, new_size))
+ return NULL;
+ }
+
+ result = mgr->pool->base + mgr->used;
+ mgr->used += needed;
+ mgr->space -= needed;
+ return result;
+}
+
+void __ast_string_field_index_build_va(struct ast_string_field_mgr *mgr,
+ ast_string_field *fields, int num_fields,
+ int index, const char *format, va_list ap1, va_list ap2)
+{
+ size_t needed;
+
+ needed = vsnprintf(mgr->pool->base + mgr->used, mgr->space, format, ap1) + 1;
+
+ va_end(ap1);
+
+ if (needed > mgr->space) {
+ size_t new_size = mgr->size * 2;
+
+ while (new_size < needed)
+ new_size *= 2;
+
+ if (add_string_pool(mgr, new_size))
+ return;
+
+ vsprintf(mgr->pool->base + mgr->used, format, ap2);
+ }
+
+ fields[index] = mgr->pool->base + mgr->used;
+ mgr->used += needed;
+ mgr->space -= needed;
+}
+
+void __ast_string_field_index_build(struct ast_string_field_mgr *mgr,
+ ast_string_field *fields, int num_fields,
+ int index, 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_index_build_va(mgr, fields, num_fields, index, format, ap1, ap2);
+
+ va_end(ap1);
+ va_end(ap2);
+}
+
+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_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;
+}
+
+int ast_dynamic_str_thread_build_va(struct ast_dynamic_str **buf, size_t max_len,
+ struct ast_threadstorage *ts, int append, const char *fmt, va_list ap)
+{
+ int res;
+ int offset = (append && (*buf)->len) ? strlen((*buf)->str) : 0;
+#if defined(DEBUG_THREADLOCALS)
+ struct ast_dynamic_str *old_buf = *buf;
+#endif /* defined(DEBUG_THREADLOCALS) */
+
+ res = vsnprintf((*buf)->str + offset, (*buf)->len - offset, fmt, ap);
+
+ /* Check to see if there was not enough space in the string buffer to prepare
+ * the string. Also, if a maximum length is present, make sure the current
+ * length is less than the maximum before increasing the size. */
+ if ((res + offset + 1) > (*buf)->len && (max_len ? ((*buf)->len < max_len) : 1)) {
+ /* Set the new size of the string buffer to be the size needed
+ * to hold the resulting string (res) plus one byte for the
+ * terminating '\0'. If this size is greater than the max, set
+ * the new length to be the maximum allowed. */
+ if (max_len)
+ (*buf)->len = ((res + offset + 1) < max_len) ? (res + offset + 1) : max_len;
+ else
+ (*buf)->len = res + offset + 1;
+
+ if (!(*buf = ast_realloc(*buf, (*buf)->len + sizeof(*(*buf)))))
+ return AST_DYNSTR_BUILD_FAILED;
+
+ if (append)
+ (*buf)->str[offset] = '\0';
+
+ if (ts) {
+ pthread_setspecific(ts->key, *buf);
+#if defined(DEBUG_THREADLOCALS)
+ __ast_threadstorage_object_replace(old_buf, *buf, (*buf)->len + sizeof(*(*buf)));
+#endif /* defined(DEBUG_THREADLOCALS) */
+ }
+
+ /* va_end() and va_start() must be done before calling
+ * vsnprintf() again. */
+ return AST_DYNSTR_BUILD_RETRY;
+ }
+
+ 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_utils_init(void)
+{
+ base64_init();
+#ifdef DEBUG_THREADS
+#if !defined(LOW_MEMORY)
+ ast_cli_register_multiple(utils_cli, sizeof(utils_cli) / sizeof(utils_cli[0]));
+#endif
+#endif
+ return 0;
+}
+
+#ifndef __AST_DEBUG_MALLOC
+int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...)
+{
+ int res;
+ va_list ap;
+
+ va_start(ap, fmt);
+ if ((res = vasprintf(ret, fmt, ap)) == -1) {
+ MALLOC_FAILURE_MSG;
+ }
+ va_end(ap);
+
+ return res;
+}
+#endif