/*
 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
 *
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 *
 * The Initial Developer of the Original Code is
 * Anthony Minessale II <anthm@freeswitch.org>
 * Portions created by the Initial Developer are Copyright (C)
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Anthony Minessale II <anthm@freeswitch.org>
 *
 *
 * switch_console.c -- Simple Console
 *
 */

#include <switch.h>
#include <switch_console.h>
#ifndef _MSC_VER
#include <switch_private.h>
#endif
#define CMD_BUFLEN 1024

#ifdef HAVE_LIBEDIT
#include <histedit.h>

static EditLine *el;
static History *myhistory;
static HistEvent ev;
static char *hfile = NULL;

#else

#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

#ifdef _MSC_VER
#define HISTLEN 10
#define KEY_UP 1
#define KEY_DOWN 2
#define KEY_TAB 3
#define CLEAR_OP 4
#define DELETE_REFRESH_OP 5
#define KEY_LEFT 6
#define KEY_RIGHT 7
#define KEY_INSERT 8
#define PROMPT_OP 9
#define KEY_DELETE 10

static int console_bufferInput(char *buf, int len, char *cmd, int key);
#endif
#endif

/*
 * store a strdup() of the string configured in XML
 * bound to each of the 12 function key
 */
static char *console_fnkeys[12];

/*
 * Load from console.conf XML file the section:
 * <keybindings>
 * <key name="1" value="show calls"/>
 * </keybindings>
 */
static switch_status_t console_xml_config(void)
{
	char *cf = "switch.conf";
	switch_xml_t cfg, xml, settings, param;

	/* clear the keybind array */
	int i;

	for (i = 0; i < 12; i++) {
		console_fnkeys[i] = NULL;
	}

	if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
		return SWITCH_STATUS_TERM;
	}

	if ((settings = switch_xml_child(cfg, "cli-keybindings"))) {
		for (param = switch_xml_child(settings, "key"); param; param = param->next) {
			char *var = (char *) switch_xml_attr_soft(param, "name");
			char *val = (char *) switch_xml_attr_soft(param, "value");
			i = atoi(var);
			if ((i < 1) || (i > 12)) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Keybind %s is invalid, range is from 1 to 12\n", var);
			} else {
				/* Add the command to the fnkey array */
				console_fnkeys[i - 1] = switch_core_permanent_strdup(val);
			}
		}
	}

	switch_xml_free(xml);

	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_stream_raw_write(switch_stream_handle_t *handle, uint8_t *data, switch_size_t datalen)
{
	switch_size_t need = handle->data_len + datalen;

	if (need >= handle->data_size) {
		void *new_data;
		need += handle->alloc_chunk;

		if (!(new_data = realloc(handle->data, need))) {
			return SWITCH_STATUS_MEMERR;
		}

		handle->data = new_data;
		handle->data_size = need;
	}

	memcpy((uint8_t *) (handle->data) + handle->data_len, data, datalen);
	handle->data_len += datalen;
	handle->end = (uint8_t *) (handle->data) + handle->data_len;
	*(uint8_t *) handle->end = '\0';

	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_stream_write(switch_stream_handle_t *handle, const char *fmt, ...)
{
	va_list ap;
	char *end = handle->end;
	int ret = 0;
	char *data = NULL;

	if (handle->data_len >= handle->data_size) {
		return SWITCH_STATUS_FALSE;
	}

	va_start(ap, fmt);
	//ret = switch_vasprintf(&data, fmt, ap);
	if (!(data = switch_vmprintf(fmt, ap))) {
		ret = -1;
	}
	va_end(ap);

	if (data) {
		switch_size_t remaining = handle->data_size - handle->data_len;
		switch_size_t need = strlen(data) + 1;

		if ((remaining < need) && handle->alloc_len) {
			switch_size_t new_len;
			void *new_data;

			new_len = handle->data_size + need + handle->alloc_chunk;
			if ((new_data = realloc(handle->data, new_len))) {
				handle->data_size = handle->alloc_len = new_len;
				handle->data = new_data;
				remaining = handle->data_size - handle->data_len;
				handle->end = (uint8_t *) (handle->data) + handle->data_len;
				end = handle->end;
			} else {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
				free(data);
				return SWITCH_STATUS_FALSE;
			}
		}

		if (remaining < need) {
			ret = -1;
		} else {
			ret = 0;
			switch_snprintf(end, remaining, "%s", data);
			handle->data_len += strlen(data);
			handle->end = (uint8_t *) (handle->data) + handle->data_len;
		}
		free(data);
	}

	return ret ? SWITCH_STATUS_FALSE : SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(switch_status_t) switch_stream_write_file_contents(switch_stream_handle_t *stream, const char *path)
{
	char *dpath = NULL;
	FILE *fd = NULL;
	switch_status_t status = SWITCH_STATUS_FALSE;

	if (!switch_is_file_path(path)) {
		dpath = switch_mprintf("%s%s%s", SWITCH_GLOBAL_dirs.conf_dir, SWITCH_PATH_SEPARATOR, path);
		path = dpath;
	}

	if ((fd = fopen(path, "r"))) {
		char *line_buf = NULL;
		switch_size_t llen = 0;

		while (switch_fp_read_dline(fd, &line_buf, &llen)) {
			stream->write_function(stream, "%s", line_buf);
		}
		fclose(fd);
		switch_safe_free(line_buf);
		status = SWITCH_STATUS_SUCCESS;
	}

	switch_safe_free(dpath);
	return status;
}

static int alias_callback(void *pArg, int argc, char **argv, char **columnNames)
{
	char **r = (char **) pArg;
	*r = strdup(argv[0]);
	return -1;
}

SWITCH_DECLARE(char *) switch_console_expand_alias(char *cmd, char *arg)
{
	char *errmsg = NULL;
	char *r = NULL;
	char *sql = NULL;
	char *exp = NULL;
	switch_cache_db_handle_t *db = NULL;
	switch_core_flag_t cflags = switch_core_flags();
	int full = 0;


	if (!(cflags & SCF_USE_SQL)) {
		return NULL;
	}

	if (switch_core_db_handle(&db) != SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database Error\n");
		return NULL;
	}


	if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
		sql = switch_mprintf("select command from aliases where alias='%q'", cmd);
	} else {
		sql = switch_mprintf("select command from aliases where alias='%w'", cmd);
	}

	switch_cache_db_execute_sql_callback(db, sql, alias_callback, &r, &errmsg);

	if (errmsg) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error [%s][%s]\n", sql, errmsg);
		free(errmsg);
	}

	switch_safe_free(sql);

	if (!r) {
		if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
			sql = switch_mprintf("select command from aliases where alias='%q %q'", cmd, arg);
		} else {
			sql = switch_mprintf("select command from aliases where alias='%w %w'", cmd, arg);
		}

		switch_cache_db_execute_sql_callback(db, sql, alias_callback, &r, &errmsg);

		if (errmsg) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error [%s][%s]\n", sql, errmsg);
			free(errmsg);
		}
		if (r) {
			full++;
		}
	}

	switch_safe_free(sql);

	if (r) {
		if (arg && !full) {
			exp = switch_mprintf("%s %s", r, arg);
			free(r);
		} else {
			exp = r;
		}
	} else {
		exp = cmd;
	}

	switch_cache_db_release_db_handle(&db);

	return exp;
}


static int switch_console_process(char *xcmd)
{
	switch_stream_handle_t stream = { 0 };
	switch_status_t status;
	FILE *handle = switch_core_get_console();
	int r = 1;

	SWITCH_STANDARD_STREAM(stream);
	switch_assert(stream.data);

	status = switch_console_execute(xcmd, 0, &stream);

	if (status == SWITCH_STATUS_SUCCESS) {
		if (handle) {
			fprintf(handle, "\n%s\n", (char *) stream.data);
			fflush(handle);
		}
	} else {
		if (!strcasecmp(xcmd, "...") || !strcasecmp(xcmd, "shutdown")) {
			r = 0;
		}
		if (handle) {
			fprintf(handle, "Unknown Command: %s\n", xcmd);
			fflush(handle);
		}
	}

	switch_safe_free(stream.data);

	return r;

}


SWITCH_DECLARE(switch_status_t) switch_console_execute(char *xcmd, int rec, switch_stream_handle_t *istream)
{
	char *arg = NULL, *alias = NULL;

	char *delim = ";;";
	int argc;
	char *argv[128];
	int x;
	char *dup = NULL;
	char *cmd;

	switch_status_t status = SWITCH_STATUS_FALSE;


	if (rec > 100) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Too much recursion!\n");
		goto end;
	}

	while (*xcmd == ' ') xcmd++;

	dup = strdup(xcmd);

	if (!strncasecmp(xcmd, "alias", 5)) {
		argc = 1;
		argv[0] = xcmd;
	} else {
		argc = switch_separate_string_string(dup, delim, argv, 128);
	}

	for (x = 0; x < argc; x++) {
		cmd = argv[x];
		if ((arg = strchr(cmd, '\r')) != 0 || (arg = strchr(cmd, '\n')) != 0) {
			*arg = '\0';
			arg = NULL;
		}
		if ((arg = strchr(cmd, ' ')) != 0) {
			*arg++ = '\0';
		}

		if ((alias = switch_console_expand_alias(cmd, arg)) && alias != cmd) {
			istream->write_function(istream, "\nExpand Alias [%s]->[%s]\n\n", cmd, alias);
			status = switch_console_execute(alias, ++rec, istream);
			free(alias);
			continue;
		}


		status = switch_api_execute(cmd, arg, NULL, istream);
	}

  end:

	switch_safe_free(dup);

	return status;
}

SWITCH_DECLARE(void) switch_console_printf(switch_text_channel_t channel, const char *file, const char *func, int line, const char *fmt, ...)
{
	char *data = NULL;
	int ret = 0;
	va_list ap;
	FILE *handle = switch_core_data_channel(channel);
	const char *filep = switch_cut_path(file);
	char date[80] = "";
	switch_size_t retsize;
	switch_time_exp_t tm;
	switch_event_t *event;

	va_start(ap, fmt);
	ret = switch_vasprintf(&data, fmt, ap);
	va_end(ap);

	if (ret == -1) {
		fprintf(stderr, "Memory Error\n");
		goto done;
	}

	if (channel == SWITCH_CHANNEL_ID_LOG_CLEAN) {
		fprintf(handle, "%s", data);
		goto done;
	}

	switch_time_exp_lt(&tm, switch_micro_time_now());
	switch_strftime_nocheck(date, &retsize, sizeof(date), "%Y-%m-%d %T", &tm);

	if (channel == SWITCH_CHANNEL_ID_LOG) {
		fprintf(handle, "[%d] %s %s:%d %s() %s", (int) getpid(), date, filep, line, func, data);
		goto done;
	}

	if (channel == SWITCH_CHANNEL_ID_EVENT &&
		switch_event_running() == SWITCH_STATUS_SUCCESS && switch_event_create(&event, SWITCH_EVENT_LOG) == SWITCH_STATUS_SUCCESS) {

		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Log-Data", data);
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Log-File", filep);
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Log-Function", func);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Log-Line", "%d", line);
		switch_event_fire(&event);
	}

  done:
	if (data) {
		free(data);
	}
	fflush(handle);
}

static int32_t running = 1;

struct helper {
	int len;
	int hits;
	int words;
	char last[512];
	char partial[512];
	FILE *out;
	switch_stream_handle_t *stream;
	switch_xml_t xml;
	int xml_off;
};

static int comp_callback(void *pArg, int argc, char **argv, char **columnNames)
{
	struct helper *h = (struct helper *) pArg;
	char *target = NULL, *str = NULL, *cur = NULL;
	switch_size_t x, y, i;


	if (argc > 0)
		target = argv[0];
	if (argc > 1)
		str = argv[1];
	if (argc > 2)
		cur = argv[2];

	if (cur) {
		while (*cur == ' ')
			cur++;
	}

	if (zstr(cur))
		cur = NULL;
	if (zstr(str))
		str = NULL;

	if (!target) {
		return -1;
	}

	if (!zstr(target) && *target == ':' && *(target + 1) == ':' && *(target + 2) == '[') {
		char *p = target + 3, *list = NULL;

		if (p) {
			char *s_argv[100] = { 0 };
			char *r_argv[1] = { 0 }, *r_cols[1] = {0};
			list = strdup(p);

			argc = switch_separate_string(list, ':', s_argv, (sizeof(s_argv) / sizeof(s_argv[0])));

			for (i = 0; (int)i < argc; i++) {
				if (!cur || !strncmp(s_argv[i], cur, strlen(cur))) {
					r_argv[0] = s_argv[i];
					comp_callback(h, 1, r_argv, r_cols);
				}
			}
			switch_safe_free(list);
		}
		return 0;
	}

	if (!zstr(target) && *target == ':' && *(target + 1) == ':') {
		char *r_argv[1] = { 0 }, *r_cols[1] = {0};
		switch_console_callback_match_t *matches;
		if (switch_console_run_complete_func(target, str, cur, &matches) == SWITCH_STATUS_SUCCESS) {
			switch_console_callback_match_node_t *m;
			for (m = matches->head; m; m = m->next) {
				if (!cur || !strncmp(m->val, cur, strlen(cur))) {
					r_argv[0] = m->val;
					comp_callback(h, 1, r_argv, r_cols);
				}
			}
			switch_console_free_matches(&matches);
		}
		return 0;
	}

	if (!zstr(target)) {
		if (h->out) {
			fprintf(h->out, "[%20s]\t", target);
		}
		if (h->stream) {
			h->stream->write_function(h->stream, "[%20s]\t", target);
		}
		if (h->xml) {
			switch_xml_set_txt_d(switch_xml_add_child_d(h->xml, "match", h->xml_off++), target);
		}

		switch_copy_string(h->last, target, sizeof(h->last));
		h->hits++;
	}

	x = strlen(h->last);
	y = strlen(h->partial);

	if (h->hits > 1) {
		for (i = 0; i < x && i < y; i++) {
			if (h->last[i] != h->partial[i]) {
				h->partial[i] = '\0';
				break;
			}
		}
	} else if (h->hits == 1) {
		switch_copy_string(h->partial, target, sizeof(h->last));
	}

	if (!zstr(target)) {
#ifdef _MSC_VER
		if ((h->hits % 3) == 0) {
#else
		if ((h->hits % 4) == 0) {
#endif
			if (h->out) {
				fprintf(h->out, "\n");
			}
			if (h->stream) {
				h->stream->write_function(h->stream, "\n");
			}
		}
	}

	return 0;
}



struct match_helper {
	switch_console_callback_match_t *my_matches;
};

static int modulename_callback(void *pArg, const char *module_name)
{
	struct match_helper *h = (struct match_helper *) pArg;

	switch_console_push_match(&h->my_matches, module_name);
	return 0;
}

SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_available_modules(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
	struct match_helper h = { 0 };

	if (switch_loadable_module_enumerate_available(SWITCH_GLOBAL_dirs.mod_dir, modulename_callback, &h) != SWITCH_STATUS_SUCCESS) {
		return SWITCH_STATUS_GENERR;
	}

	if (h.my_matches) {
		*matches = h.my_matches;
		return SWITCH_STATUS_SUCCESS;
	}

	return SWITCH_STATUS_FALSE;
}

SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_loaded_modules(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
	struct match_helper h = { 0 };

	if (switch_loadable_module_enumerate_loaded(modulename_callback, &h) != SWITCH_STATUS_SUCCESS) {
		return SWITCH_STATUS_GENERR;
	}

	if (h.my_matches) {
		*matches = h.my_matches;
		return SWITCH_STATUS_SUCCESS;
	}

	return SWITCH_STATUS_FALSE;
}

#ifdef HAVE_GETIFADDRS
#include <ifaddrs.h>
#include <net/if.h>
SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_interfaces(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
	struct match_helper h = { 0 };
	struct ifaddrs *addrs, *addr;

	getifaddrs(&addrs);
	for(addr = addrs; addr; addr = addr->ifa_next) {
		if (addr->ifa_flags & IFF_UP) {
			switch_console_push_match_unique(&h.my_matches, addr->ifa_name);
		}
	}
	freeifaddrs(addrs);

	if (h.my_matches) {
		*matches = h.my_matches;
		return SWITCH_STATUS_SUCCESS;
	}

	return SWITCH_STATUS_FALSE;
}
#else
SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_interfaces(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
	return SWITCH_STATUS_FALSE;
}
#endif

static int uuid_callback(void *pArg, int argc, char **argv, char **columnNames)
{
	struct match_helper *h = (struct match_helper *) pArg;

	switch_console_push_match(&h->my_matches, argv[0]);
	return 0;

}

SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_uuid(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
	char *sql;
	struct match_helper h = { 0 };
	switch_cache_db_handle_t *db = NULL;
	switch_status_t status = SWITCH_STATUS_FALSE;
	char *errmsg;


	if (switch_core_db_handle(&db) != SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database Error\n");
		return SWITCH_STATUS_GENERR;
	}

	if (!zstr(cursor)) {
		sql = switch_mprintf("select distinct uuid from channels where uuid like '%q%%' and hostname='%q' order by uuid",
							 cursor, switch_core_get_switchname());
	} else {
		sql = switch_mprintf("select distinct uuid from channels where hostname='%q' order by uuid", switch_core_get_switchname());
	}

	switch_cache_db_execute_sql_callback(db, sql, uuid_callback, &h, &errmsg);
	free(sql);

	switch_cache_db_release_db_handle(&db);

	if (h.my_matches) {
		*matches = h.my_matches;
		status = SWITCH_STATUS_SUCCESS;
	}


	return status;
}


SWITCH_DECLARE(unsigned char) switch_console_complete(const char *line, const char *cursor, FILE * console_out,
													  switch_stream_handle_t *stream, switch_xml_t xml)
{
	switch_cache_db_handle_t *db = NULL;
	char *sql = NULL;
	char *dup = strdup(line);
	char *buf = dup;
	char *p, *lp = NULL;
	char *errmsg = NULL;
	struct helper h = { 0 };
	unsigned char ret = CC_REDISPLAY;
	int pos = 0;
	int sc = 0;

#ifndef HAVE_LIBEDIT
#ifndef _MSC_VER
	if (!stream) {
		return CC_ERROR;
	}
#endif
#endif

	if (switch_core_db_handle(&db) != SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database Error\n");
		switch_safe_free(dup);
		return CC_ERROR;
	}

	if (!zstr(cursor) && !zstr(line)) {
		pos = (int)(cursor - line);
	}

	h.out = console_out;
	h.stream = stream;
	h.xml = xml;

	if (pos > 0) {
		*(buf + pos) = '\0';
	}

	if ((p = strchr(buf, '\r')) || (p = strchr(buf, '\n'))) {
		*p = '\0';
	}

	while (*buf == ' ') {
		sc++;
		buf++;
	}

	if (!*buf) {
#ifdef HAVE_LIBEDIT
		if (h.out && sc) {
			el_deletestr(el, sc);
		}
#endif
	}

	sc = 0;
	p = end_of_p(buf);
	while (p >= buf && *p == ' ') {
		sc++;
		p--;
	}

	if (sc > 1) {
#ifdef HAVE_LIBEDIT
		if (h.out) {
			el_deletestr(el, sc - 1);
		}
#endif
		*(p + 2) = '\0';
	}

	for (p = buf; p && *p; p++) {
		if (*p == ' ') {
			lp = p;
			h.words++;
			while (*p == ' ')
				p++;
			if (!*p)
				break;
		}
	}

	if (lp) {
		buf = lp + 1;
	}

	h.len = (int)strlen(buf);

	if (h.out) {
		fprintf(h.out, "\n\n");
	}

	if (h.stream) {
		h.stream->write_function(h.stream, "\n\n");
	}



	if (h.words == 0) {
		sql = switch_mprintf("select distinct name from interfaces where type='api' and name like '%q%%' and hostname='%q' order by name",
							 buf, switch_core_get_hostname());
	}

	if (sql) {
		switch_cache_db_execute_sql_callback(db, sql, comp_callback, &h, &errmsg);

		if (errmsg) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error [%s][%s]\n", sql, errmsg);
			free(errmsg);
			ret = CC_ERROR;
			goto end;
		}
		free(sql);
		sql = NULL;
	}

	if (h.hits != -1) {
		char *dupdup = strdup(dup);
		int x, argc = 0;
		char *argv[10] = { 0 };
		switch_stream_handle_t stream = { 0 };
		SWITCH_STANDARD_STREAM(stream);
		switch_assert(dupdup);

		argc = switch_separate_string(dupdup, ' ', argv, (sizeof(argv) / sizeof(argv[0])));

		if (h.words == 0) {
			stream.write_function(&stream, "select distinct a1 from complete where " "a1 not in (select name from interfaces where hostname='%q') %s ",
								  switch_core_get_hostname(), argc ? "and" : "");
		} else {
			if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
				stream.write_function(&stream, "select distinct a%d,'%q','%q' from complete where ", h.words + 1, switch_str_nil(dup), switch_str_nil(lp));
			} else {
				stream.write_function(&stream, "select distinct a%d,'%q','%w' from complete where ", h.words + 1, switch_str_nil(dup), switch_str_nil(lp));
			}
		}

		for (x = 0; x < argc; x++) {
			if (h.words + 1 > argc) {
				if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
					stream.write_function(&stream, "(a%d like '::%%' or a%d = '' or a%d = '%q')%q",
										  x + 1, x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and ");
				} else {
					stream.write_function(&stream, "(a%d like '::%%' or a%d = '' or a%d = '%w')%w",
										  x + 1, x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and ");
				}
			} else {
				if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
					stream.write_function(&stream, "(a%d like '::%%' or a%d = '' or a%d like '%q%%')%q",
										  x + 1, x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and ");
				} else {
					stream.write_function(&stream, "(a%d like '::%%' or a%d = '' or a%d like '%w%%')%w",
										  x + 1, x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and ");
				}
			}
		}

		stream.write_function(&stream, " and hostname='%s' order by a%d", switch_core_get_hostname(), h.words + 1);

		switch_cache_db_execute_sql_callback(db, stream.data, comp_callback, &h, &errmsg);

		if (errmsg) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error [%s][%s]\n", (char *) stream.data, errmsg);
			free(errmsg);
			ret = CC_ERROR;
		}

		switch_safe_free(dupdup);
		switch_safe_free(stream.data);

		if (ret == CC_ERROR) {
			goto end;
		}
	}

	if (h.out) {
		fprintf(h.out, "\n\n");
	}

	if (h.stream) {
		h.stream->write_function(h.stream, "\n\n");
		if (h.hits == 1 && !zstr(h.last)) {
			h.stream->write_function(h.stream, "write=%d:%s ", h.len, h.last);
		} else if (h.hits > 1 && !zstr(h.partial)) {
			h.stream->write_function(h.stream, "write=%d:%s", h.len, h.partial);
		}
	}

	if (h.xml) {
		switch_xml_t x_write = switch_xml_add_child_d(h.xml, "write", h.xml_off++);
		char buf[32];

		snprintf(buf, sizeof(buf), "%d", h.len);
		switch_xml_set_attr_d_buf(x_write, "length", buf);

		if (h.hits == 1 && !zstr(h.last)) {
			switch_xml_set_txt_d(x_write, h.last);
		} else if (h.hits > 1 && !zstr(h.partial)) {
			switch_xml_set_txt_d(x_write, h.partial);
		}
	}
#ifdef HAVE_LIBEDIT
	if (h.out) {
		if (h.hits == 1 && !zstr(h.last)) {
			el_deletestr(el, h.len);
			el_insertstr(el, h.last);
			el_insertstr(el, " ");
		} else if (h.hits > 1 && !zstr(h.partial)) {
			el_deletestr(el, h.len);
			el_insertstr(el, h.partial);
		}
	}
#else
#ifdef _MSC_VER
	if (h.out) {
		if (h.hits == 1 && !zstr(h.last)) {
			console_bufferInput(0, h.len, (char *) line, DELETE_REFRESH_OP);
			console_bufferInput(h.last, (int) strlen(h.last), (char *) line, 0);
			console_bufferInput(" ", 1, (char *) line, 0);
		} else if (h.hits > 1 && !zstr(h.partial)) {
			console_bufferInput(0, h.len, (char *) line, DELETE_REFRESH_OP);
			console_bufferInput(h.partial, (int) strlen(h.partial), (char *) line, 0);
		} else {
			console_bufferInput(0, 0, (char *) line, DELETE_REFRESH_OP);
		}
	}
#endif
#endif

  end:

	if (h.out) {
		fflush(h.out);
	}

	switch_safe_free(sql);
	switch_safe_free(dup);

	switch_cache_db_release_db_handle(&db);

	return (ret);
}


#if defined(HAVE_LIBEDIT) || defined(_MSC_VER)
/*
 * If a fnkey is configured then process the command
 */
static unsigned char console_fnkey_pressed(int i)
{
	char *c, *cmd;

	switch_assert((i > 0) && (i <= 12));

	c = console_fnkeys[i - 1];

	/* This new line is necessary to avoid output to begin after the ">" of the CLI's prompt */
	switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_CONSOLE, "\n");

	if (c == NULL) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "FUNCTION KEY F%d IS NOT BOUND, please edit switch.conf XML file\n", i);
		return CC_REDISPLAY;
	}

	cmd = strdup(c);
	switch_console_process(cmd);
	free(cmd);

	return CC_REDISPLAY;
}
#endif

SWITCH_DECLARE(void) switch_console_save_history(void)
{
#ifdef HAVE_LIBEDIT
	history(myhistory, &ev, H_SAVE, hfile);
#else
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "NOT IMPLEMENTED!\n");
#endif
}

#ifdef HAVE_LIBEDIT
static char prompt_str[512] = "";

static unsigned char console_f1key(EditLine * el, int ch)
{
	return console_fnkey_pressed(1);
}
static unsigned char console_f2key(EditLine * el, int ch)
{
	return console_fnkey_pressed(2);
}
static unsigned char console_f3key(EditLine * el, int ch)
{
	return console_fnkey_pressed(3);
}
static unsigned char console_f4key(EditLine * el, int ch)
{
	return console_fnkey_pressed(4);
}
static unsigned char console_f5key(EditLine * el, int ch)
{
	return console_fnkey_pressed(5);
}
static unsigned char console_f6key(EditLine * el, int ch)
{
	return console_fnkey_pressed(6);
}
static unsigned char console_f7key(EditLine * el, int ch)
{
	return console_fnkey_pressed(7);
}
static unsigned char console_f8key(EditLine * el, int ch)
{
	return console_fnkey_pressed(8);
}
static unsigned char console_f9key(EditLine * el, int ch)
{
	return console_fnkey_pressed(9);
}
static unsigned char console_f10key(EditLine * el, int ch)
{
	return console_fnkey_pressed(10);
}
static unsigned char console_f11key(EditLine * el, int ch)
{
	return console_fnkey_pressed(11);
}
static unsigned char console_f12key(EditLine * el, int ch)
{
	return console_fnkey_pressed(12);
}


char *prompt(EditLine * e)
{
	if (*prompt_str == '\0') {
		switch_snprintf(prompt_str, sizeof(prompt_str), "freeswitch@%s> ", switch_core_get_switchname());
	}

	return prompt_str;
}

static void *SWITCH_THREAD_FUNC console_thread(switch_thread_t *thread, void *obj)
{
	int count;
	const char *line;
	switch_memory_pool_t *pool = (switch_memory_pool_t *) obj;

	while (running) {
		int32_t arg = 0;

		if (getppid() == 1) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "We've become an orphan, no more console for us.\n");
			break;
		}

		switch_core_session_ctl(SCSC_CHECK_RUNNING, &arg);
		if (!arg) {
			break;
		}

		line = el_gets(el, &count);

		if (count > 1) {
			if (!zstr(line)) {
				char *cmd = strdup(line);
				char *p;
				const LineInfo *lf = el_line(el);
				char *foo = (char *) lf->buffer;
				if ((p = strrchr(cmd, '\r')) || (p = strrchr(cmd, '\n'))) {
					*p = '\0';
				}
				assert(cmd != NULL);
				history(myhistory, &ev, H_ENTER, line);
				running = switch_console_process(cmd);
				el_deletestr(el, strlen(foo) + 1);
				memset(foo, 0, strlen(foo));
				free(cmd);
			}
		}
		switch_cond_next();
	}

	switch_core_destroy_memory_pool(&pool);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Editline thread exiting\n");
	return NULL;
}

static unsigned char complete(EditLine * el, int ch)
{
	const LineInfo *lf = el_line(el);

	return switch_console_complete(lf->buffer, lf->cursor, switch_core_get_console(), NULL, NULL);
}


SWITCH_DECLARE(void) switch_console_loop(void)
{
	switch_thread_t *thread;
	switch_threadattr_t *thd_attr = NULL;
	switch_memory_pool_t *pool;

	if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Pool Failure\n");
		return;
	}

	el = el_init(__FILE__, switch_core_get_console(), switch_core_get_console(), switch_core_get_console());
	el_set(el, EL_PROMPT, &prompt);
	el_set(el, EL_EDITOR, "emacs");
	/* AGX: Bind Keyboard function keys. This has been tested with:
	 * - linux console keyabord
	 * - putty.exe connected via ssh to linux
	 */
	/* Load/Init the config first */
	console_xml_config();
	/* Bind the functions to the key */
	el_set(el, EL_ADDFN, "f1-key", "F1 KEY PRESS", console_f1key);
	el_set(el, EL_ADDFN, "f2-key", "F2 KEY PRESS", console_f2key);
	el_set(el, EL_ADDFN, "f3-key", "F3 KEY PRESS", console_f3key);
	el_set(el, EL_ADDFN, "f4-key", "F4 KEY PRESS", console_f4key);
	el_set(el, EL_ADDFN, "f5-key", "F5 KEY PRESS", console_f5key);
	el_set(el, EL_ADDFN, "f6-key", "F6 KEY PRESS", console_f6key);
	el_set(el, EL_ADDFN, "f7-key", "F7 KEY PRESS", console_f7key);
	el_set(el, EL_ADDFN, "f8-key", "F8 KEY PRESS", console_f8key);
	el_set(el, EL_ADDFN, "f9-key", "F9 KEY PRESS", console_f9key);
	el_set(el, EL_ADDFN, "f10-key", "F10 KEY PRESS", console_f10key);
	el_set(el, EL_ADDFN, "f11-key", "F11 KEY PRESS", console_f11key);
	el_set(el, EL_ADDFN, "f12-key", "F12 KEY PRESS", console_f12key);

	el_set(el, EL_BIND, "\033OP", "f1-key", NULL);
	el_set(el, EL_BIND, "\033OQ", "f2-key", NULL);
	el_set(el, EL_BIND, "\033OR", "f3-key", NULL);
	el_set(el, EL_BIND, "\033OS", "f4-key", NULL);


	el_set(el, EL_BIND, "\033[11~", "f1-key", NULL);
	el_set(el, EL_BIND, "\033[12~", "f2-key", NULL);
	el_set(el, EL_BIND, "\033[13~", "f3-key", NULL);
	el_set(el, EL_BIND, "\033[14~", "f4-key", NULL);
	el_set(el, EL_BIND, "\033[15~", "f5-key", NULL);
	el_set(el, EL_BIND, "\033[17~", "f6-key", NULL);
	el_set(el, EL_BIND, "\033[18~", "f7-key", NULL);
	el_set(el, EL_BIND, "\033[19~", "f8-key", NULL);
	el_set(el, EL_BIND, "\033[20~", "f9-key", NULL);
	el_set(el, EL_BIND, "\033[21~", "f10-key", NULL);
	el_set(el, EL_BIND, "\033[23~", "f11-key", NULL);
	el_set(el, EL_BIND, "\033[24~", "f12-key", NULL);


	el_set(el, EL_ADDFN, "ed-complete", "Complete argument", complete);
	el_set(el, EL_BIND, "^I", "ed-complete", NULL);

	/* "Delete" key. */
	el_set(el, EL_BIND, "\033[3~", "ed-delete-next-char", NULL);

	myhistory = history_init();
	if (myhistory == 0) {
		fprintf(stderr, "history could not be initialized\n");
		return;
	}

	hfile = switch_mprintf("%s%sfreeswitch.history", SWITCH_GLOBAL_dirs.log_dir, SWITCH_PATH_SEPARATOR);
	assert(hfile != NULL);

	history(myhistory, &ev, H_SETSIZE, 800);
	el_set(el, EL_HIST, history, myhistory);
	history(myhistory, &ev, H_LOAD, hfile);

	el_source(el, NULL);

	switch_threadattr_create(&thd_attr, pool);
	switch_threadattr_detach_set(thd_attr, 1);
	switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
	switch_thread_create(&thread, thd_attr, console_thread, pool, pool);

	while (running) {
		int32_t arg = 0;
		switch_core_session_ctl(SCSC_CHECK_RUNNING, &arg);
		if (!arg) {
			break;
		}
		switch_yield(1000000);
	}

	history(myhistory, &ev, H_SAVE, hfile);
	free(hfile);

	/* Clean up our memory */
	history_end(myhistory);
	el_end(el);
}

#else

#ifdef _MSC_VER
char history[HISTLEN][CMD_BUFLEN + 1];
int iHistory = 0;
int iHistorySel = 0;

static int console_history(char *cmd, int direction)
{
	int i;
	static int first;

	if (direction == 0) {
		first = 1;
		if (iHistory < HISTLEN) {
			if (iHistory && strcmp(history[iHistory - 1], cmd)) {
				iHistorySel = iHistory;
				strcpy(history[iHistory++], cmd);
			} else if (iHistory == 0) {
				iHistorySel = iHistory;
				strcpy(history[iHistory++], cmd);
			}
		} else {
			iHistory = HISTLEN - 1;
			for (i = 0; i < HISTLEN - 1; i++) {
				strcpy(history[i], history[i + 1]);
			}
			iHistorySel = iHistory;
			strcpy(history[iHistory++], cmd);
		}
	} else {
		if (!first) {
			iHistorySel += direction;
		}
		first = 0;
		if (iHistorySel < 0) {
			iHistorySel = 0;
		}
		if (iHistory && iHistorySel >= iHistory) {
			iHistorySel = iHistory - 1;
		}
		strcpy(cmd, history[iHistorySel]);
	}
	return (SWITCH_STATUS_SUCCESS);
}

static int console_bufferInput(char *addchars, int len, char *cmd, int key)
{
	static int iCmdBuffer = 0;
	static int iCmdCursor = 0;
	static int ignoreNext = 0;
	static int insertMode = 1;
	static COORD orgPosition;
	static char prompt[80];
	int iBuf;
	int i;

	HANDLE hOut;
	CONSOLE_SCREEN_BUFFER_INFO info;
	COORD position;
	hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	GetConsoleScreenBufferInfo(hOut, &info);
	position = info.dwCursorPosition;
	if (iCmdCursor == 0) {
		orgPosition = position;
	}

	if (key == PROMPT_OP) {
		if (strlen(cmd) < sizeof(prompt)) {
			strcpy(prompt, cmd);
		}
		return 0;
	}

	if (key == KEY_TAB) {
		switch_console_complete(cmd, cmd + iCmdBuffer, switch_core_get_console(), NULL, NULL);
		return 0;
	}
	if (key == KEY_UP || key == KEY_DOWN || key == CLEAR_OP) {
		SetConsoleCursorPosition(hOut, orgPosition);
		for (i = 0; i < (int) strlen(cmd); i++) {
			printf(" ");
		}
		SetConsoleCursorPosition(hOut, orgPosition);
		iCmdBuffer = 0;
		iCmdCursor = 0;
		memset(cmd, 0, CMD_BUFLEN);
	}
	if (key == DELETE_REFRESH_OP) {
		int l = len < (int) strlen(cmd) ? len : (int) strlen(cmd);
		for (i = 0; i < l; i++) {
			cmd[--iCmdBuffer] = 0;
		}
		iCmdCursor = (int) strlen(cmd);
		printf("%s", prompt);
		GetConsoleScreenBufferInfo(hOut, &info);
		orgPosition = info.dwCursorPosition;
		printf("%s", cmd);
		return 0;
	}

	if (key == KEY_LEFT) {
		if (iCmdCursor) {
			if (position.X == 0) {
				position.Y -= 1;
				position.X = info.dwSize.X - 1;
			} else {
				position.X -= 1;
			}

			SetConsoleCursorPosition(hOut, position);
			iCmdCursor--;
		}
	}
	if (key == KEY_RIGHT) {
		if (iCmdCursor < (int) strlen(cmd)) {
			if (position.X == info.dwSize.X - 1) {
				position.Y += 1;
				position.X = 0;
			} else {
				position.X += 1;
			}

			SetConsoleCursorPosition(hOut, position);
			iCmdCursor++;
		}
	}
	if (key == KEY_INSERT) {
		insertMode = !insertMode;
	}
	if (key == KEY_DELETE) {
		if (iCmdCursor < iCmdBuffer) {
			int pos;
			for (pos = iCmdCursor; pos < iCmdBuffer; pos++) {
				cmd[pos] = cmd[pos + 1];
			}
			cmd[pos] = 0;
			iCmdBuffer--;

			for (pos = iCmdCursor; pos < iCmdBuffer; pos++) {
				printf("%c", cmd[pos]);
			}
			printf(" ");
			SetConsoleCursorPosition(hOut, position);
		}
	}
	for (iBuf = 0; iBuf < len; iBuf++) {
		switch (addchars[iBuf]) {
		case '\r':
		case '\n':
			if (ignoreNext) {
				ignoreNext = 0;
			} else {
				int ret = iCmdBuffer;
				if (iCmdBuffer == 0) {
					strcpy(cmd, "Empty");
					ret = (int) strlen(cmd);
				} else {
					console_history(cmd, 0);
					cmd[iCmdBuffer] = 0;
				}
				iCmdBuffer = 0;
				iCmdCursor = 0;
				printf("\n");
				return (ret);
			}
			break;
		case '\b':
			if (iCmdCursor) {
				if (position.X == 0) {
					position.Y -= 1;
					position.X = info.dwSize.X - 1;
					SetConsoleCursorPosition(hOut, position);
				} else {
					position.X -= 1;
					SetConsoleCursorPosition(hOut, position);
				}
				printf(" ");
				if (iCmdCursor < iCmdBuffer) {
					int pos;
					iCmdCursor--;
					for (pos = iCmdCursor; pos < iCmdBuffer; pos++) {
						cmd[pos] = cmd[pos + 1];
					}
					cmd[pos] = 0;
					iCmdBuffer--;

					SetConsoleCursorPosition(hOut, position);
					for (pos = iCmdCursor; pos < iCmdBuffer; pos++) {
						printf("%c", cmd[pos]);
					}
					printf(" ");
					SetConsoleCursorPosition(hOut, position);
				} else {
					SetConsoleCursorPosition(hOut, position);
					iCmdBuffer--;
					iCmdCursor--;
					cmd[iCmdBuffer] = 0;
				}
			}
			break;
		default:
			if (!ignoreNext) {
				if (iCmdCursor < iCmdBuffer) {
					int pos;

					if (position.X == info.dwSize.X - 1) {
						position.Y += 1;
						position.X = 0;
					} else {
						position.X += 1;
					}

					if (insertMode) {
						for (pos = iCmdBuffer - 1; pos >= iCmdCursor; pos--) {
							cmd[pos + 1] = cmd[pos];
						}
					}
					iCmdBuffer++;
					cmd[iCmdCursor++] = addchars[iBuf];
					printf("%c", addchars[iBuf]);
					for (pos = iCmdCursor; pos < iCmdBuffer; pos++) {
						GetConsoleScreenBufferInfo(hOut, &info);
						if (info.dwCursorPosition.X == info.dwSize.X - 1 && info.dwCursorPosition.Y == info.dwSize.Y - 1) {
							orgPosition.Y -= 1;
							position.Y -= 1;
						}
						printf("%c", cmd[pos]);
					}
					SetConsoleCursorPosition(hOut, position);
				} else {
					if (position.X == info.dwSize.X - 1 && position.Y == info.dwSize.Y - 1) {
						orgPosition.Y -= 1;
					}
					cmd[iCmdBuffer++] = addchars[iBuf];
					iCmdCursor++;
					printf("%c", addchars[iBuf]);
				}
			}
		}
		if (iCmdBuffer == CMD_BUFLEN) {
			printf("Read Console... BUFFER OVERRUN\n");
			iCmdBuffer = 0;
			ignoreNext = 1;
		}
	}
	return (SWITCH_STATUS_SUCCESS);
}


static BOOL console_readConsole(HANDLE conIn, char *buf, int len, int *pRed, int *key)
{
	DWORD recordIndex, bufferIndex, toRead = 0, red;
	PINPUT_RECORD pInput;

	GetNumberOfConsoleInputEvents(conIn, &toRead);
	if (len < (int) toRead) {
		toRead = len;
	}
	if (toRead == 0) {
		return (FALSE);
	}

	if ((pInput = (PINPUT_RECORD) malloc(toRead * sizeof(INPUT_RECORD))) == NULL) {
		return (FALSE);
	}
	*key = 0;
	ReadConsoleInput(conIn, pInput, toRead, &red);

	for (recordIndex = bufferIndex = 0; recordIndex < red; recordIndex++) {
		KEY_EVENT_RECORD keyEvent = pInput[recordIndex].Event.KeyEvent;
		if (pInput[recordIndex].EventType == KEY_EVENT && keyEvent.bKeyDown) {
			if (keyEvent.wVirtualKeyCode == 38 && keyEvent.wVirtualScanCode == 72) {
				buf[0] = 0;
				console_history(buf, -1);
				*key = KEY_UP;
				bufferIndex += (DWORD) strlen(buf);
			}
			if (keyEvent.wVirtualKeyCode == 40 && keyEvent.wVirtualScanCode == 80) {
				buf[0] = 0;
				console_history(buf, 1);
				*key = KEY_DOWN;
				bufferIndex += (DWORD) strlen(buf);
			}
			if (keyEvent.wVirtualKeyCode == 112 && keyEvent.wVirtualScanCode == 59) {
				console_fnkey_pressed(1);
			}
			if (keyEvent.wVirtualKeyCode == 113 && keyEvent.wVirtualScanCode == 60) {
				console_fnkey_pressed(2);
			}
			if (keyEvent.wVirtualKeyCode == 114 && keyEvent.wVirtualScanCode == 61) {
				console_fnkey_pressed(3);
			}
			if (keyEvent.wVirtualKeyCode == 115 && keyEvent.wVirtualScanCode == 62) {
				console_fnkey_pressed(4);
			}
			if (keyEvent.wVirtualKeyCode == 116 && keyEvent.wVirtualScanCode == 63) {
				console_fnkey_pressed(5);
			}
			if (keyEvent.wVirtualKeyCode == 117 && keyEvent.wVirtualScanCode == 64) {
				console_fnkey_pressed(6);
			}
			if (keyEvent.wVirtualKeyCode == 118 && keyEvent.wVirtualScanCode == 65) {
				console_fnkey_pressed(7);
			}
			if (keyEvent.wVirtualKeyCode == 119 && keyEvent.wVirtualScanCode == 66) {
				console_fnkey_pressed(8);
			}
			if (keyEvent.wVirtualKeyCode == 120 && keyEvent.wVirtualScanCode == 67) {
				console_fnkey_pressed(9);
			}
			if (keyEvent.wVirtualKeyCode == 121 && keyEvent.wVirtualScanCode == 68) {
				console_fnkey_pressed(10);
			}
			if (keyEvent.wVirtualKeyCode == 122 && keyEvent.wVirtualScanCode == 87) {
				console_fnkey_pressed(11);
			}
			if (keyEvent.wVirtualKeyCode == 123 && keyEvent.wVirtualScanCode == 88) {
				console_fnkey_pressed(12);
			}
			if (keyEvent.uChar.AsciiChar == 9) {
				*key = KEY_TAB;
				break;
			}
			if (keyEvent.uChar.AsciiChar == 27) {
				*key = CLEAR_OP;
				break;
			}
			if (keyEvent.wVirtualKeyCode == 37 && keyEvent.wVirtualScanCode == 75) {
				*key = KEY_LEFT;
			}
			if (keyEvent.wVirtualKeyCode == 39 && keyEvent.wVirtualScanCode == 77) {
				*key = KEY_RIGHT;
			}
			if (keyEvent.wVirtualKeyCode == 45 && keyEvent.wVirtualScanCode == 82) {
				*key = KEY_INSERT;
			}
			if (keyEvent.wVirtualKeyCode == 46 && keyEvent.wVirtualScanCode == 83) {
				*key = KEY_DELETE;
			}
			while (keyEvent.wRepeatCount && keyEvent.uChar.AsciiChar) {
				buf[bufferIndex] = keyEvent.uChar.AsciiChar;
				if (buf[bufferIndex] == '\r') {
					buf[bufferIndex] = '\n';
				}
				bufferIndex++;
				keyEvent.wRepeatCount--;
			}
		}
	}

	free(pInput);
	*pRed = bufferIndex;
	return (TRUE);
}
#endif


SWITCH_DECLARE(void) switch_console_loop(void)
{
	char cmd[CMD_BUFLEN + 1] = "";
	int32_t activity = 1;
#ifndef _MSC_VER
	int x = 0;
#else
	char keys[CMD_BUFLEN];
#endif

	/* Load/Init the config first */
	console_xml_config();

#ifdef _MSC_VER
	sprintf(cmd, "\nfreeswitch@%s> ", switch_core_get_switchname());
	console_bufferInput(0, 0, cmd, PROMPT_OP);
	memset(cmd, 0, sizeof(cmd));
#endif

	while (running) {
		int32_t arg;
#ifdef _MSC_VER
		int read, key;
		HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
#else
		fd_set rfds, efds;
		struct timeval tv = { 0, 20000 };
#endif

		switch_core_session_ctl(SCSC_CHECK_RUNNING, &arg);
		if (!arg) {
			break;
		}

		if (activity) {
			switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_CONSOLE, "\nfreeswitch@%s> ", switch_core_get_switchname());
		}
#ifdef _MSC_VER
		activity = 0;
		if (console_readConsole(stdinHandle, keys, (int) sizeof(keys), &read, &key)) {
			if (console_bufferInput(keys, read, cmd, key)) {
				if (!strcmp(cmd, "Empty")) {
					cmd[0] = 0;
				}
				activity = 1;
				if (cmd[0]) {
					running = switch_console_process(cmd);
				}
				memset(cmd, 0, sizeof(cmd));
			}
		}
		Sleep(20);
#else
		FD_ZERO(&rfds);
		FD_ZERO(&efds);
		FD_SET(fileno(stdin), &rfds);
		FD_SET(fileno(stdin), &efds);
		if ((activity = select(fileno(stdin) + 1, &rfds, NULL, &efds, &tv)) < 0) {
			break;
		}

		if (FD_ISSET(fileno(stdin), &efds)) {
			continue;
		}

		if (!FD_ISSET(fileno(stdin), &rfds)) {
			activity = 0;
		}

		if (activity == 0) {
			fflush(stdout);
			continue;
		}

		memset(&cmd, 0, sizeof(cmd));
		for (x = 0; x < (sizeof(cmd) - 1); x++) {
			int c = getchar();
			if (c < 0) {
				int y = read(fileno(stdin), cmd, sizeof(cmd) - 1);
				cmd[y - 1] = '\0';
				break;
			}

			cmd[x] = (char) c;

			if (cmd[x] == '\n') {
				cmd[x] = '\0';
				break;
			}
		}

		if (cmd[0]) {
			running = switch_console_process(cmd);
		}
#endif
	}
}



#endif


static struct {
	switch_hash_t *func_hash;
	switch_mutex_t *func_mutex;
} globals;

SWITCH_DECLARE(switch_status_t) switch_console_init(switch_memory_pool_t *pool)
{
	switch_mutex_init(&globals.func_mutex, SWITCH_MUTEX_NESTED, pool);
	switch_core_hash_init(&globals.func_hash);
	switch_console_add_complete_func("::console::list_available_modules", (switch_console_complete_callback_t) switch_console_list_available_modules);
	switch_console_add_complete_func("::console::list_loaded_modules", (switch_console_complete_callback_t) switch_console_list_loaded_modules);
	switch_console_add_complete_func("::console::list_interfaces", (switch_console_complete_callback_t) switch_console_list_interfaces);
	switch_console_add_complete_func("::console::list_uuid", (switch_console_complete_callback_t) switch_console_list_uuid);
	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(switch_status_t) switch_console_shutdown(void)
{
	return switch_core_hash_destroy(&globals.func_hash);
}

SWITCH_DECLARE(switch_status_t) switch_console_add_complete_func(const char *name, switch_console_complete_callback_t cb)
{
	switch_status_t status;

	switch_mutex_lock(globals.func_mutex);
	status = switch_core_hash_insert(globals.func_hash, name, (void *) (intptr_t) cb);
	switch_mutex_unlock(globals.func_mutex);

	return status;
}

SWITCH_DECLARE(switch_status_t) switch_console_del_complete_func(const char *name)
{
	switch_status_t status;

	switch_mutex_lock(globals.func_mutex);
	status = switch_core_hash_insert(globals.func_hash, name, NULL);
	switch_mutex_unlock(globals.func_mutex);

	return status;
}

SWITCH_DECLARE(void) switch_console_free_matches(switch_console_callback_match_t **matches)
{
	switch_console_callback_match_t *my_match = *matches;
	switch_console_callback_match_node_t *m, *cur;

	/* Don't play with matches */
	*matches = NULL;

	m = my_match->head;
	while (m) {
		cur = m;
		m = m->next;
		free(cur->val);
		free(cur);
	}

	if (my_match->dynamic) {
		free(my_match);
	}
}

SWITCH_DECLARE(void) switch_console_sort_matches(switch_console_callback_match_t *matches)
{
	switch_console_callback_match_node_t *p = NULL, *sort[4] = { 0 };
	int i, j;

	switch_assert(matches);

	if (matches->count < 2) {
		return;
	}

	for (i = 1; i < matches->count; i++) {
		sort[0] = NULL;
		sort[1] = matches->head;
		sort[2] = sort[1] ? sort[1]->next : NULL;
		sort[3] = sort[2] ? sort[2]->next : NULL;

		for (j = 1; j <= (matches->count - i); j++) {
			switch_assert(sort[1] && sort[2]);
			if (strcmp(sort[1]->val, sort[2]->val) > 0) {
				sort[1]->next = sort[3];
				sort[2]->next = sort[1];

				if (sort[0])
					sort[0]->next = sort[2];
				if (sort[1] == matches->head)
					matches->head = sort[2];




				sort[0] = sort[2];
				sort[2] = sort[1]->next;
				if (sort[3] && sort[3]->next)
					sort[3] = sort[3]->next;

			} else {
				sort[0] = sort[1];
				sort[1] = sort[2];
				sort[2] = sort[3];
				if (sort[3] && sort[3]->next)
					sort[3] = sort[3]->next;
			}

		}
	}

	p = matches->head;

	for (i = 1; i < matches->count; i++)
		p = p->next;

	if (p) {
		p->next = NULL;
		matches->end = p;
	}
}

SWITCH_DECLARE(void) switch_console_push_match_unique(switch_console_callback_match_t **matches, const char *new_val)
{
	/* Ignore the entry if it is already in the list */
	if (*matches) {
		switch_console_callback_match_node_t *node;

		for(node = (*matches)->head; node; node = node->next) {
			if (!strcasecmp(node->val, new_val)) return;
		}
	}

	switch_console_push_match(matches, new_val);
}

SWITCH_DECLARE(void) switch_console_push_match(switch_console_callback_match_t **matches, const char *new_val)
{
	switch_console_callback_match_node_t *match;

	if (!*matches) {
		switch_zmalloc(*matches, sizeof(**matches));
		(*matches)->dynamic = 1;
	}

	switch_zmalloc(match, sizeof(*match));
	match->val = strdup(new_val);

	if ((*matches)->head) {
		(*matches)->end->next = match;
	} else {
		(*matches)->head = match;
	}

	(*matches)->count++;

	(*matches)->end = match;
}

SWITCH_DECLARE(switch_status_t) switch_console_run_complete_func(const char *func, const char *line, const char *last_word,
																 switch_console_callback_match_t **matches)
{
	switch_console_complete_callback_t cb;
	switch_status_t status = SWITCH_STATUS_FALSE;

	switch_mutex_lock(globals.func_mutex);
	if ((cb = (switch_console_complete_callback_t) (intptr_t) switch_core_hash_find(globals.func_hash, func))) {
		if ((status = cb(line, last_word, matches)) == SWITCH_STATUS_SUCCESS) {
			switch_console_sort_matches(*matches);
		}
	}
	switch_mutex_unlock(globals.func_mutex);

	return status;
}


SWITCH_DECLARE(switch_status_t) switch_console_set_complete(const char *string)
{
	char *mydata = NULL, *argv[11] = { 0 };
	int argc, x;
	switch_status_t status = SWITCH_STATUS_FALSE;
	switch_core_flag_t cflags = switch_core_flags();

	if (!(cflags & SCF_USE_SQL)) {
		return SWITCH_STATUS_FALSE;
	}

	if (string && (mydata = strdup(string))) {
		if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
			switch_stream_handle_t mystream = { 0 };
			SWITCH_STANDARD_STREAM(mystream);

			if (!strcasecmp(argv[0], "stickyadd")) {
				mystream.write_function(&mystream, "insert into complete (sticky, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, hostname) values (1,");
				for (x = 0; x < 10; x++) {
					if (argv[x + 1] && !strcasecmp(argv[x + 1], "_any_")) {
						mystream.write_function(&mystream, "%s", "'', ");
					} else {
						if (switch_core_dbtype() == SCDB_TYPE_CORE_DB) {
							mystream.write_function(&mystream, "'%q', ", switch_str_nil(argv[x + 1]));
						} else {
							mystream.write_function(&mystream, "'%w', ", switch_str_nil(argv[x + 1]));
						}
					}
				}
				mystream.write_function(&mystream, " '%s')", switch_core_get_hostname());
				switch_core_sql_exec(mystream.data);
				status = SWITCH_STATUS_SUCCESS;
			} else if (!strcasecmp(argv[0], "add")) {
				mystream.write_function(&mystream, "insert into complete (sticky, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, hostname) values (0,");
				for (x = 0; x < 10; x++) {
					if (argv[x + 1] && !strcasecmp(argv[x + 1], "_any_")) {
						mystream.write_function(&mystream, "%s", "'', ");
					} else {
						if (switch_core_dbtype() == SCDB_TYPE_CORE_DB) {
							mystream.write_function(&mystream, "'%q', ", switch_str_nil(argv[x + 1]));
						} else {
							mystream.write_function(&mystream, "'%w', ", switch_str_nil(argv[x + 1]));
						}
					}
				}
				mystream.write_function(&mystream, " '%s')", switch_core_get_hostname());

				switch_core_sql_exec(mystream.data);
				status = SWITCH_STATUS_SUCCESS;
			} else if (!strcasecmp(argv[0], "del")) {
				char *what = argv[1];
				if (zstr(what)) {
					switch_safe_free(mystream.data);
					switch_safe_free(mydata);
					return SWITCH_STATUS_FALSE;
				} else if (!strcasecmp(what, "*")) {
					mystream.write_function(&mystream, "delete from complete where hostname='%q'", switch_core_get_hostname());
					switch_core_sql_exec(mystream.data);
				} else {
					mystream.write_function(&mystream, "delete from complete where ");
					for (x = 0; x < argc - 1; x++) {
						if (switch_core_dbtype() == SCDB_TYPE_CORE_DB) {
							mystream.write_function(&mystream, "a%d = '%q'%q", x + 1, switch_str_nil(argv[x + 1]), x == argc - 2 ? "" : " and ");
						} else {
							mystream.write_function(&mystream, "a%d = '%w'%w", x + 1, switch_str_nil(argv[x + 1]), x == argc - 2 ? "" : " and ");
						}
					}
					mystream.write_function(&mystream, " and hostname='%q'", switch_core_get_hostname());
					switch_core_sql_exec(mystream.data);
				}
				status = SWITCH_STATUS_SUCCESS;
			}

			switch_safe_free(mystream.data);
		}
	}

	switch_safe_free(mydata);

	return status;

}


SWITCH_DECLARE(switch_status_t) switch_console_set_alias(const char *string)
{
	char *mydata = NULL, *argv[3] = { 0 };
	int argc;
	switch_status_t status = SWITCH_STATUS_FALSE;

	if (string && (mydata = strdup(string))) {
		if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) >= 2) {
			switch_cache_db_handle_t *db = NULL;
			char *sql = NULL;

			if (argc > 2 && !strcmp(argv[1], argv[2])) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Alias and command cannot be the same, this will cause loop!\n");
				return SWITCH_STATUS_FALSE;
			}

			if (switch_core_db_handle(&db) != SWITCH_STATUS_SUCCESS) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database Error\n");
				free(mydata);
				return SWITCH_STATUS_FALSE;
			}

			if (!strcasecmp(argv[0], "stickyadd") && argc == 3) {
				sql = switch_mprintf("delete from aliases where alias='%q' and hostname='%q'", argv[1], switch_core_get_switchname());
				switch_cache_db_persistant_execute(db, sql, 5);
				switch_safe_free(sql);
				if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
					sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (1, '%q','%q','%q')",
										 argv[1], argv[2], switch_core_get_switchname());
				} else {
					sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (1, '%w','%w','%w')",
										 argv[1], argv[2], switch_core_get_switchname());
				}
				switch_cache_db_persistant_execute(db, sql, 5);
				status = SWITCH_STATUS_SUCCESS;
			} else if (!strcasecmp(argv[0], "add") && argc == 3) {
				sql = switch_mprintf("delete from aliases where alias='%q' and hostname='%q'", argv[1], switch_core_get_switchname());
				switch_cache_db_persistant_execute(db, sql, 5);
				switch_safe_free(sql);
				if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
					sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (0, '%q','%q','%q')",
										 argv[1], argv[2], switch_core_get_switchname());
				} else {
					sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (0, '%w','%w','%w')",
										 argv[1], argv[2], switch_core_get_switchname());
				}
				switch_cache_db_persistant_execute(db, sql, 5);
				status = SWITCH_STATUS_SUCCESS;
			} else if (!strcasecmp(argv[0], "del") && argc == 2) {
				char *what = argv[1];
				if (!strcasecmp(what, "*")) {
					sql = switch_mprintf("delete from aliases where hostname='%q'", switch_core_get_switchname());
					switch_cache_db_persistant_execute(db, sql, 1);
				} else {
					sql = switch_mprintf("delete from aliases where alias='%q' and hostname='%q'", argv[1], switch_core_get_switchname());
					switch_cache_db_persistant_execute(db, sql, 5);
				}
				status = SWITCH_STATUS_SUCCESS;
			}
			switch_safe_free(sql);
			switch_cache_db_release_db_handle(&db);
		}
	}

	switch_safe_free(mydata);

	return status;

}




/* For Emacs:
 * Local Variables:
 * mode:c
 * indent-tabs-mode:t
 * tab-width:4
 * c-basic-offset:4
 * End:
 * For VIM:
 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
 */