/******************************************************************************
* TinTin++ *
* Copyright (C) 2004 (See CREDITS file) *
* *
* This program is protected under the GNU GPL (See COPYING) *
* *
* 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 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 *
*******************************************************************************/
/******************************************************************************
* (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t *
* *
* coded by Peter Unold 1992 *
* recoded by Igor van den Hoven 2004 *
******************************************************************************/
#include "tintin.h"
struct listroot *init_list(struct session *ses, int type, int size)
{
struct listroot *listhead;
if ((listhead = (struct listroot *) calloc(1, sizeof(struct listroot))) == NULL)
{
fprintf(stderr, "couldn't alloc listhead\n");
exit(1);
}
listhead->ses = ses;
listhead->list = (struct listnode **) calloc(size, sizeof(struct listnode *));
listhead->size = size;
listhead->type = type;
listhead->flags = list_table[type].flags;
return listhead;
}
void kill_list(struct listroot *root)
{
while (root->used)
{
delete_index_list(root, root->used - 1);
}
}
void free_list(struct listroot *root)
{
kill_list(root);
free(root->list);
free(root);
}
struct listroot *copy_list(struct session *ses, struct listroot *sourcelist, int type)
{
int i;
struct listnode *node;
push_call("copy_list(%p,%p,%p)",ses,sourcelist,type);
ses->list[type] = init_list(ses, type, sourcelist->size);
if (HAS_BIT(sourcelist->flags, LIST_FLAG_INHERIT))
{
for (i = 0 ; i < sourcelist->used ; i++)
{
node = (struct listnode *) calloc(1, sizeof(struct listnode));
node->left = strdup(sourcelist->list[i]->left);
node->right = strdup(sourcelist->list[i]->right);
node->pr = strdup(sourcelist->list[i]->pr);
node->group = strdup(sourcelist->list[i]->group);
switch (type)
{
case LIST_ALIAS:
node->regex = tintin_regexp_compile(node, node->left, PCRE_ANCHORED);
break;
case LIST_ACTION:
case LIST_GAG:
case LIST_HIGHLIGHT:
case LIST_PROMPT:
case LIST_SUBSTITUTE:
node->regex = tintin_regexp_compile(node, node->left, 0);
break;
case LIST_VARIABLE:
copy_nest_node(ses->list[type], node, sourcelist->list[i]);
break;
}
ses->list[type]->list[i] = node;
}
ses->list[type]->used = sourcelist->used;
}
ses->list[type]->flags = sourcelist->flags;
pop_call();
return ses->list[type];
}
/*
create a node and stuff it into the list in the desired order
*/
struct listnode *insert_node_list(struct listroot *root, char *ltext, char *rtext, char *prtext)
{
int index;
struct listnode *node;
node = (struct listnode *) calloc(1, sizeof(struct listnode));
node->left = strdup(ltext);
node->right = strdup(rtext);
node->pr = strdup(prtext);
node->group = HAS_BIT(root->flags, LIST_FLAG_CLASS) ? strdup(root->ses->group) : strdup("");
switch (root->type)
{
case LIST_ALIAS:
node->regex = tintin_regexp_compile(node, node->left, PCRE_ANCHORED);
break;
case LIST_ACTION:
case LIST_GAG:
case LIST_HIGHLIGHT:
case LIST_PROMPT:
case LIST_SUBSTITUTE:
node->regex = tintin_regexp_compile(node, node->left, 0);
break;
}
index = locate_index_list(root, ltext, prtext);
return insert_index_list(root, node, index);
}
struct listnode *update_node_list(struct listroot *root, char *ltext, char *rtext, char *prtext)
{
int index;
struct listnode *node;
index = search_index_list(root, ltext, NULL);
if (index != -1)
{
node = root->list[index];
if (strcmp(node->right, rtext) != 0)
{
free(node->right);
node->right = strdup(rtext);
}
node->data = 0;
switch (list_table[root->type].mode)
{
case PRIORITY:
if (atof(node->pr) != atof(prtext))
{
delete_index_list(root, index);
return insert_node_list(root, ltext, rtext, prtext);
}
break;
case APPEND:
delete_index_list(root, index);
return insert_node_list(root, ltext, rtext, prtext);
break;
case ALPHA:
if (strcmp(node->pr, prtext) != 0)
{
free(node->pr);
node->pr = strdup(prtext);
}
break;
default:
tintin_printf2(root->ses, "#BUG: update_node_list: unknown mode: %d", list_table[root->type].mode);
break;
}
return node;
}
else
{
return insert_node_list(root, ltext, rtext, prtext);
}
}
struct listnode *insert_index_list(struct listroot *root, struct listnode *node, int index)
{
root->used++;
if (root->used == root->size)
{
root->size *= 2;
root->list = (struct listnode **) realloc(root->list, (root->size) * sizeof(struct listnode *));
}
memmove(&root->list[index + 1], &root->list[index], (root->used - index) * sizeof(struct listnode *));
root->list[index] = node;
return node;
}
void delete_node_list(struct session *ses, int type, struct listnode *node)
{
int index = search_index_list(ses->list[type], node->left, node->pr);
delete_index_list(ses->list[type], index);
}
void delete_index_list(struct listroot *root, int index)
{
struct listnode *node = root->list[index];
if (node->root)
{
free_list(node->root);
}
if (index <= root->update)
{
root->update--;
}
free(node->left);
free(node->right);
free(node->pr);
free(node->group);
if (node->regex)
{
free(node->regex);
}
free(node);
memmove(&root->list[index], &root->list[index + 1], (root->used - index) * sizeof(struct listnode *));
root->used--;
return;
}
struct listnode *search_node_list(struct listroot *root, char *text)
{
int index;
switch (list_table[root->type].mode)
{
case ALPHA:
index = bsearch_alpha_list(root, text, 0);
break;
default:
index = nsearch_list(root, text);
break;
}
if (index != -1)
{
return root->list[index];
}
else
{
return NULL;
}
}
int search_index_list(struct listroot *root, char *text, char *priority)
{
if (list_table[root->type].mode == ALPHA)
{
return bsearch_alpha_list(root, text, 0);
}
if (list_table[root->type].mode == PRIORITY && priority)
{
return bsearch_priority_list(root, text, priority, 0);
}
return nsearch_list(root, text);
}
/*
Return insertion index.
*/
int locate_index_list(struct listroot *root, char *text, char *priority)
{
switch (list_table[root->type].mode)
{
case ALPHA:
return bsearch_alpha_list(root, text, 1);
case PRIORITY:
return bsearch_priority_list(root, text, priority, 1);
default:
return root->used;
}
}
/*
Yup, all this for a bloody binary search.
*/
int bsearch_alpha_list(struct listroot *root, char *text, int seek)
{
long long bot, top, val;
double toi, toj, srt;
bot = 0;
top = root->used - 1;
val = top;
toi = get_number(root->ses, text);
if (!seek && toi && (*text == '+' || *text == '-') && HAS_BIT(list_table[root->type].flags, LIST_FLAG_NEST))
{
if (toi > 0 && toi <= root->used)
{
return toi - 1;
}
if (toi < 0 && toi + root->used >= 0)
{
return root->used + toi;
}
else
{
return -1;
}
}
while (bot <= top)
{
toj = get_number(root->ses, root->list[val]->left);
if (toi)
{
srt = toi - toj;
}
else if (toj)
{
srt = -1;
}
else
{
srt = strcmp(text, root->list[val]->left);
}
if (srt == 0)
{
return val;
}
if (srt < 0)
{
top = val - 1;
}
else
{
bot = val + 1;
}
val = bot + (top - bot) / 2;
}
if (seek)
{
return UMAX(0, val);
}
else
{
return -1;
}
}
int bsearch_priority_list(struct listroot *root, char *text, char *priority, int seek)
{
int bot, top, val;
double srt;
bot = 0;
top = root->used - 1;
val = top;
while (bot <= top)
{
srt = atof(priority) - atof(root->list[val]->pr);
if (!srt)
{
srt = strcmp(text, root->list[val]->left);
}
if (srt == 0)
{
return val;
}
if (srt < 0)
{
top = val - 1;
}
else
{
bot = val + 1;
}
val = bot + (top - bot) / 2;
}
if (seek)
{
return UMAX(0, val);
}
else
{
return -1;
}
}
int nsearch_list(struct listroot *root, char *text)
{
int i;
for (i = 0 ; i < root->used ; i++)
{
if (!strcmp(text, root->list[i]->left))
{
return i;
}
}
return -1;
}
/*
show contens of a node on screen
*/
void show_node(struct listroot *root, struct listnode *node, int level)
{
char arg[STRING_SIZE], buf[STRING_SIZE], out[STRING_SIZE];
show_nest_node(node, arg, TRUE);
switch (list_table[root->type].args)
{
case 3:
sprintf(buf, "%*s#%s <118>{<088>%s<118>}<168>=<118>{<088>%s<118>} <168>@ <118>{<088>%s<118>}", level * 2, "", list_table[root->type].name, node->left, arg, node->pr);
break;
case 2:
sprintf(buf, "%*s#%s <118>{<088>%s<118>}<168>=<118>{<088>%s<118>}", level * 2, "", list_table[root->type].name, node->left, arg);
break;
case 1:
sprintf(buf, "%*s#%s <118>{<088>%s<118>}", level * 2, "", list_table[root->type].name, node->left);
break;
default:
sprintf(buf, "#BUG: list_table[type].args == 0");
break;
}
substitute(root->ses, buf, out, SUB_COL);
tintin_printf2(root->ses, "%s", out);
}
/*
list contens of a list on screen
*/
void show_list(struct listroot *root, int level)
{
int i;
if (root == root->ses->list[root->type])
{
tintin_header(root->ses, " %s ", list_table[root->type].name_multi);
}
for (i = 0 ; i < root->used ; i++)
{
show_node(root, root->list[i], level);
}
}
int show_node_with_wild(struct session *ses, char *text, int type)
{
struct listroot *root = ses->list[type];
struct listnode *node;
int i, flag = FALSE;
node = search_node_list(root, text);
if (node)
{
show_node(root, node, 0);
return TRUE;
}
for (i = 0 ; i < root->used ; i++)
{
if (match(NULL, root->list[i]->left, text))
{
show_node(root, root->list[i], 0);
flag = TRUE;
}
}
return flag;
}
void delete_node_with_wild(struct session *ses, int type, char *text)
{
struct listroot *root = ses->list[type];
struct listnode *node;
char arg1[BUFFER_SIZE];
int i, found = FALSE;
sub_arg_in_braces(ses, text, arg1, 1, SUB_VAR|SUB_FUN);
node = search_node_list(root, arg1);
if (node)
{
show_message(ses, type, "#OK. {%s} IS NO LONGER %s %s.", node->left, (*list_table[type].name == 'A' || *list_table[type].name == 'E') ? "AN" : "A", list_table[type].name);
delete_node_list(ses, type, node);
return;
}
for (i = root->used - 1 ; i >= 0 ; i--)
{
if (match(NULL, root->list[i]->left, arg1))
{
show_message(ses, type, "#OK. {%s} IS NO LONGER %s %s.", root->list[i]->left, (*list_table[type].name == 'A' || *list_table[type].name == 'E') ? "AN" : "A", list_table[type].name);
delete_index_list(root, i);
found = TRUE;
}
}
if (found == 0)
{
show_message(ses, type, "#KILL: NO MATCHES FOUND FOR %s {%s}.", list_table[type].name, arg1);
}
}
DO_COMMAND(do_kill)
{
char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
int index;
arg = get_arg_in_braces(arg, arg1, 0);
get_arg_in_braces(arg, arg2, 1);
if (*arg1 == 0 || !strcasecmp(arg1, "ALL"))
{
for (index = 0 ; index < LIST_MAX ; index++)
{
kill_list(ses->list[index]);
}
tintin_printf(ses, "#KILL - ALL LISTS CLEARED.");
return ses;
}
for (index = 0 ; index < LIST_MAX ; index++)
{
if (!is_abbrev(arg1, list_table[index].name_multi))
{
continue;
}
if (*arg2 == 0 || !strcasecmp(arg2, "ALL"))
{
kill_list(ses->list[index]);
tintin_printf(ses, "#OK: #%s LIST CLEARED.", list_table[index].name);
}
else
{
delete_node_with_wild(ses, index, arg);
}
break;
}
if (index == LIST_MAX)
{
tintin_printf(ses, "#ERROR: #KILL {%s} {%s} - NO MATCH FOUND.", arg1, arg2);
}
return ses;
}
DO_COMMAND(do_message)
{
char left[BUFFER_SIZE], right[BUFFER_SIZE];
int index, found = FALSE;
arg = get_arg_in_braces(arg, left, FALSE);
arg = get_arg_in_braces(arg, right, FALSE);
if (*left == 0)
{
tintin_header(ses, " MESSAGES ");
for (index = 0 ; index < LIST_MAX ; index++)
{
tintin_printf2(ses, " %-20s %3s", list_table[index].name_multi, HAS_BIT(ses->list[index]->flags, LIST_FLAG_MESSAGE) ? "ON" : "OFF");
}
tintin_header(ses, "");
}
else
{
for (index = found = 0 ; index < LIST_MAX ; index++)
{
if (!is_abbrev(left, list_table[index].name_multi) && strcasecmp(left, "ALL"))
{
continue;
}
if (*right == 0)
{
TOG_BIT(ses->list[index]->flags, LIST_FLAG_MESSAGE);
}
else if (is_abbrev(right, "ON"))
{
SET_BIT(ses->list[index]->flags, LIST_FLAG_MESSAGE);
}
else if (is_abbrev(right, "OFF"))
{
DEL_BIT(ses->list[index]->flags, LIST_FLAG_MESSAGE);
}
else
{
tintin_printf(ses, "#SYNTAX: #MESSAGE [NAME] [ON|OFF]");
break;
}
tintin_printf(ses, "#OK: #%s MESSAGES HAVE BEEN SET TO: %s.", list_table[index].name, HAS_BIT(ses->list[index]->flags, LIST_FLAG_MESSAGE) ? "ON" : "OFF");
found = TRUE;
}
if (found == FALSE)
{
tintin_printf(ses, "#ERROR: #MESSAGE {%s} - NO MATCH FOUND.", left);
}
}
return ses;
}
DO_COMMAND(do_ignore)
{
char left[BUFFER_SIZE], right[BUFFER_SIZE];
int index, found = FALSE;
arg = get_arg_in_braces(arg, left, 0);
arg = get_arg_in_braces(arg, right, 0);
if (*left == 0)
{
tintin_header(ses, " IGNORES ");
for (index = 0 ; index < LIST_MAX ; index++)
{
tintin_printf2(ses, " %-20s %3s", list_table[index].name_multi, HAS_BIT(ses->list[index]->flags, LIST_FLAG_IGNORE) ? "ON" : "OFF");
}
tintin_header(ses, "");
}
else
{
for (index = found = 0 ; index < LIST_MAX ; index++)
{
if (!is_abbrev(left, list_table[index].name_multi) && strcasecmp(left, "ALL"))
{
continue;
}
if (*right == 0)
{
TOG_BIT(ses->list[index]->flags, LIST_FLAG_IGNORE);
}
else if (is_abbrev(right, "ON"))
{
SET_BIT(ses->list[index]->flags, LIST_FLAG_IGNORE);
}
else if (is_abbrev(right, "OFF"))
{
DEL_BIT(ses->list[index]->flags, LIST_FLAG_IGNORE);
}
else
{
tintin_printf(ses, "#SYNTAX: #IGNORE [NAME] [ON|OFF]");
break;
}
tintin_printf(ses, "#OK: #%s IGNORE STATUS HAS BEEN SET TO: %s.", list_table[index].name, HAS_BIT(ses->list[index]->flags, LIST_FLAG_IGNORE) ? "ON" : "OFF");
found = TRUE;
}
if (found == FALSE)
{
tintin_printf(ses, "#ERROR: #IGNORE {%s} - NO MATCH FOUND.", left);
}
}
return ses;
}
DO_COMMAND(do_debug)
{
char left[BUFFER_SIZE], right[BUFFER_SIZE];
int index, found = FALSE;
arg = get_arg_in_braces(arg, left, 0);
arg = get_arg_in_braces(arg, right, 0);
if (*left == 0)
{
tintin_header(ses, " DEBUGS ");
for (index = 0 ; index < LIST_MAX ; index++)
{
tintin_printf2(ses, " %-20s %3s", list_table[index].name_multi, HAS_BIT(ses->list[index]->flags, LIST_FLAG_DEBUG) ? "ON" : "OFF");
}
tintin_header(ses, "");
}
else
{
for (index = found = 0 ; index < LIST_MAX ; index++)
{
if (!is_abbrev(left, list_table[index].name_multi) && strcasecmp(left, "ALL"))
{
continue;
}
if (*right == 0)
{
TOG_BIT(ses->list[index]->flags, LIST_FLAG_DEBUG);
}
else if (is_abbrev(right, "ON"))
{
SET_BIT(ses->list[index]->flags, LIST_FLAG_DEBUG);
}
else if (is_abbrev(right, "OFF"))
{
DEL_BIT(ses->list[index]->flags, LIST_FLAG_DEBUG);
}
else if (is_abbrev(right, "LOG"))
{
SET_BIT(ses->list[index]->flags, LIST_FLAG_LOG);
}
else
{
tintin_printf(ses, "#SYNTAX: #DEBUG [NAME] [ON|OFF|LOG]");
break;
}
tintin_printf(ses, "#OK: #%s DEBUG STATUS HAS BEEN SET TO: %s.", list_table[index].name, is_abbrev(right, "LOG") ? "LOG" : HAS_BIT(ses->list[index]->flags, LIST_FLAG_DEBUG) ? "ON" : "OFF");
found = TRUE;
}
if (found == FALSE)
{
tintin_printf2(ses, "#DEBUG {%s} - NO MATCH FOUND.", left);
}
}
return ses;
}