/****************************************************************************** * 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 Igor van den Hoven 2008 * ******************************************************************************/ #include "tintin.h" struct scriptdata { long long min; long long max; long long cnt; int inc; char * cpy; char * str; char * arg; }; struct scriptnode { struct scriptnode * next; struct scriptnode * prev; union { struct scriptdata * data; struct script_regex * regex; }; char * str; short lvl; short type; short cmd; }; struct script_regex { char * str; char * bod; char * buf; int val; }; struct scriptroot { struct scriptnode * next; struct scriptnode * prev; struct session * ses; }; void debugtoken(struct session *ses, struct scriptnode *token) { push_call("debugtoken(%p,%p,%d)",ses,token,token->type); if (ses->debug_level) { switch (token->type) { case TOKEN_TYPE_STRING: case TOKEN_TYPE_SESSION: tintin_printf2(ses, "[%02d] %*s%s", token->type, token->lvl * 4, "", token->str); break; case TOKEN_TYPE_ELSE: case TOKEN_TYPE_END: tintin_printf2(ses, "[%02d] %*s\033[1;32m%s\033[0m", token->type, token->lvl * 4, "", token->str); break; case TOKEN_TYPE_DEFAULT: tintin_printf2(ses, "[%02d] %*s\033[1;32m%s\033[0m", token->type, token->lvl * 4, "", command_table[token->cmd].name); break; case TOKEN_TYPE_BREAK: case TOKEN_TYPE_CONTINUE: tintin_printf2(ses, "[%02d] %*s\033[1;31m%s\033[0m", token->type, token->lvl * 4, "", command_table[token->cmd].name); break; case TOKEN_TYPE_COMMAND: tintin_printf2(ses, "[%02d] %*s\033[1;36m%s\033[0m %s\033[0m", token->type, token->lvl * 4, "", command_table[token->cmd].name, token->str); break; case TOKEN_TYPE_RETURN: tintin_printf2(ses, "[%02d] %*s\033[1;31m%s\033[0m %s\033[0m", token->type, token->lvl * 4, "", command_table[token->cmd].name, token->str); break; case TOKEN_TYPE_CASE: case TOKEN_TYPE_ELSEIF: case TOKEN_TYPE_IF: case TOKEN_TYPE_FOREACH: case TOKEN_TYPE_LOOP: case TOKEN_TYPE_PARSE: case TOKEN_TYPE_SWITCH: case TOKEN_TYPE_WHILE: tintin_printf2(ses, "[%02d] %*s\033[1;32m%s {\033[0m%s\033[1;32m}\033[0m", token->type, token->lvl * 4, "", command_table[token->cmd].name, token->str); break; case TOKEN_TYPE_REGEX: tintin_printf2(ses, "[%02d] %*s\033[1;32m%s {\033[0m%s\033[1;32m} {\033[0m%s\033[1;32m}\033[0m", token->type, token->lvl * 4, "", command_table[token->cmd].name, token->str, token->regex->str); break; default: if (token == (struct scriptnode *) ses) { tintin_printf2(ses, "[--] (error) token == ses"); } else { tintin_printf2(ses, "[%02d] %*s\033[1;33m%d {\033[0m%s\033[1;32m}\033[0m", token->type, token->lvl * 4, "", token->cmd, token->str); } break; } } pop_call(); return; } void addtoken(struct scriptroot *root, int lvl, int opr, int cmd, char *str) { struct scriptnode *token; token = (struct scriptnode *) calloc(1, sizeof(struct scriptnode)); token->lvl = lvl; token->type = opr; token->cmd = cmd; token->str = strdup(str); LINK(token, root->next, root->prev); } char *addlooptoken(struct scriptroot *root, int lvl, int opr, int cmd, char *str) { struct scriptdata *data; char min[BUFFER_SIZE], max[BUFFER_SIZE], var[BUFFER_SIZE]; data = (struct scriptdata *) calloc(1, sizeof(struct scriptdata)); str = get_arg_in_braces(str, min, FALSE); str = get_arg_in_braces(str, max, FALSE); str = get_arg_in_braces(str, var, FALSE); data->cpy = refstring(NULL, "{%s} {%s} {%s}", min, max, var); addtoken(root, lvl, opr, cmd, var); data->str = strdup(""); root->prev->data = data; return str; } void resetlooptoken(struct session *ses, struct scriptnode *token) { char *str, min[BUFFER_SIZE], max[BUFFER_SIZE]; str = token->data->cpy; str = get_arg_in_braces(str, min, FALSE); str = get_arg_in_braces(str, max, FALSE); token->data->min = (int) get_number(ses, min); token->data->max = (int) get_number(ses, max); token->data->inc = token->data->min <= token->data->max ? 1 : -1; token->data->cnt = token->data->min; } void breaklooptoken(struct scriptnode *token) { token->data->min = token->data->max = token->data->cnt = token->data->inc = 0; } char *addforeachtoken(struct scriptroot *root, int lvl, int opr, int cmd, char *str) { struct scriptdata *data; char arg[BUFFER_SIZE], var[BUFFER_SIZE]; str = get_arg_in_braces(str, arg, FALSE); str = get_arg_in_braces(str, var, FALSE); addtoken(root, lvl, opr, cmd, var); data = (struct scriptdata *) calloc(1, sizeof(struct scriptdata)); data->cpy = refstring(NULL, "{%s} {%s}", arg, var); data->str = strdup(""); data->arg = data->str; root->prev->data = data; return str; } void resetforeachtoken(struct session *ses, struct scriptnode *token) { char *str, arg[BUFFER_SIZE]; str = token->data->cpy; str = sub_arg_in_braces(ses, str, arg, GET_ONE, SUB_VAR|SUB_FUN); RESTRING(token->data->str, arg); token->data->arg = token->data->str; } void breakforeachtoken(struct scriptnode *token) { RESTRING(token->data->str, ""); token->data->arg = token->data->str; } void handlereturntoken(struct session *ses, struct scriptnode *token) { char arg[BUFFER_SIZE]; substitute(ses, token->str, arg, SUB_VAR|SUB_FUN); set_nest_node(ses->list[LIST_VARIABLE], "result", "%s", arg); } char *get_arg_foreach(struct scriptnode *token) { static char buf[BUFFER_SIZE]; token->data->arg = get_arg_in_braces(token->data->arg, buf, TRUE); if (*token->data->arg == COMMAND_SEPARATOR) { token->data->arg++; } return buf; } char *addparsetoken(struct scriptroot *root, int lvl, int opr, int cmd, char *str) { struct scriptdata *data; char arg[BUFFER_SIZE], var[BUFFER_SIZE]; str = get_arg_in_braces(str, arg, FALSE); str = get_arg_in_braces(str, var, FALSE); addtoken(root, lvl, opr, cmd, var); data = (struct scriptdata *) calloc(1, sizeof(struct scriptdata)); data->cpy = refstring(NULL, "{%s} {%s}", arg, var); data->str = strdup(""); data->arg = data->str; root->prev->data = data; return str; } void resetparsetoken(struct session *ses, struct scriptnode *token) { char *str, arg[BUFFER_SIZE]; str = token->data->cpy; str = sub_arg_in_braces(ses, str, arg, GET_ONE, SUB_VAR|SUB_FUN); RESTRING(token->data->str, arg); token->data->arg = token->data->str; } void breakparsetoken(struct scriptnode *token) { RESTRING(token->data->str, ""); token->data->arg = token->data->str; } char *get_arg_parse(struct scriptnode *token) { static char buf[3]; buf[2] = 0; buf[0] = *token->data->arg++; #ifdef BIG5 if (buf[0] & 0x80 && *token->data->arg) { buf[1] = *token->data->arg++; } #endif return buf; } char *addregextoken(struct scriptroot *root, int lvl, int type, int cmd, char *str) { struct script_regex *regex; char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE]; str = get_arg_in_braces(str, arg1, FALSE); str = get_arg_in_braces(str, arg2, FALSE); str = get_arg_in_braces(str, arg3, TRUE); addtoken(root, lvl, type, cmd, arg1); regex = (struct script_regex *) calloc(1, sizeof(struct script_regex)); regex->str = strdup(arg2); regex->bod = strdup(arg3); regex->buf = calloc(1, BUFFER_SIZE); root->prev->regex = regex; return str; } void deltoken(struct scriptroot *root, struct scriptnode *token) { push_call("deltoken(%p,%p)",root,token); UNLINK(token, root->next, root->prev); free(token->str); switch (token->type) { case TOKEN_TYPE_REGEX: free(token->regex->str); free(token->regex->bod); free(token->regex->buf); free(token->regex); break; case TOKEN_TYPE_LOOP: case TOKEN_TYPE_FOREACH: case TOKEN_TYPE_PARSE: free(token->data->cpy); free(token->data->str); free(token->data); break; } free(token); pop_call(); return; } int find_command(char *command) { struct session *ses; int cmd; for (ses = gts ; ses ; ses = ses->next) { if (!strcmp(ses->name, command)) { return -1; } } if (isalpha((int) *command)) { for (cmd = gtd->command_ref[tolower((int) *command) - 'a'] ; *command_table[cmd].name ; cmd++) { if (is_abbrev(command, command_table[cmd].name)) { return cmd; } } } return -1; } void tokenize_script(struct scriptroot *root, int lvl, char *str) { char *arg, *line; int cmd; if (*str == 0) { addtoken(root, lvl, TOKEN_TYPE_STRING, -1, ""); return; } line = (char *) calloc(1, BUFFER_SIZE); while (*str) { if (!VERBATIM(root->ses)) { str = space_out(str); } if (*str != gtd->tintin_char) { str = get_arg_all(str, line, VERBATIM(root->ses) ? GET_VBT : GET_ALL); addtoken(root, lvl, TOKEN_TYPE_STRING, -1, line); } else { arg = get_arg_stop_spaces(str, line, 0); cmd = find_command(line+1); if (cmd == -1) { str = get_arg_all(str, line, GET_ALL); addtoken(root, lvl, TOKEN_TYPE_SESSION, -1, line+1); } else { switch (command_table[cmd].type) { case TOKEN_TYPE_BREAK: str = get_arg_with_spaces(arg, line, 1); addtoken(root, lvl, TOKEN_TYPE_BREAK, cmd, line); break; case TOKEN_TYPE_CASE: str = get_arg_in_braces(arg, line, FALSE); addtoken(root, lvl++, TOKEN_TYPE_CASE, cmd, line); str = get_arg_in_braces(str, line, TRUE); tokenize_script(root, lvl--, line); addtoken(root, lvl, TOKEN_TYPE_END, -1, "endcase"); break; case TOKEN_TYPE_CONTINUE: str = get_arg_with_spaces(arg, line, 1); addtoken(root, lvl, TOKEN_TYPE_CONTINUE, cmd, line); break; case TOKEN_TYPE_DEFAULT: addtoken(root, lvl++, TOKEN_TYPE_DEFAULT, cmd, ""); str = get_arg_in_braces(arg, line, TRUE); tokenize_script(root, lvl--, line); addtoken(root, lvl, TOKEN_TYPE_END, -1, "enddefault"); break; case TOKEN_TYPE_ELSE: addtoken(root, lvl++, TOKEN_TYPE_ELSE, cmd, "else"); str = get_arg_in_braces(arg, line, TRUE); tokenize_script(root, lvl--, line); addtoken(root, lvl, TOKEN_TYPE_END, -1, "endelse"); break; case TOKEN_TYPE_ELSEIF: str = get_arg_in_braces(arg, line, FALSE); addtoken(root, lvl++, TOKEN_TYPE_ELSEIF, cmd, line); str = get_arg_in_braces(str, line, TRUE); tokenize_script(root, lvl--, line); addtoken(root, lvl, TOKEN_TYPE_END, -1, "endif"); break; case TOKEN_TYPE_FOREACH: str = addforeachtoken(root, lvl++, TOKEN_TYPE_FOREACH, cmd, arg); str = get_arg_in_braces(str, line, TRUE); tokenize_script(root, lvl--, line); addtoken(root, lvl, TOKEN_TYPE_END, -1, "endforeach"); break; case TOKEN_TYPE_IF: str = get_arg_in_braces(arg, line, FALSE); addtoken(root, lvl++, TOKEN_TYPE_IF, cmd, line); str = get_arg_in_braces(str, line, TRUE); tokenize_script(root, lvl--, line); addtoken(root, lvl, TOKEN_TYPE_END, -1, "endif"); if (*str && *str != COMMAND_SEPARATOR) { addtoken(root, lvl++, TOKEN_TYPE_ELSE, -1, "else"); str = get_arg_in_braces(str, line, TRUE); tokenize_script(root, lvl--, line); addtoken(root, lvl, TOKEN_TYPE_END, -1, "endif"); } break; case TOKEN_TYPE_LOOP: str = addlooptoken(root, lvl++, TOKEN_TYPE_LOOP, cmd, arg); str = get_arg_in_braces(str, line, TRUE); tokenize_script(root, lvl--, line); addtoken(root, lvl, TOKEN_TYPE_END, -1, "endloop"); break; case TOKEN_TYPE_PARSE: str = addparsetoken(root, lvl++, TOKEN_TYPE_PARSE, cmd, arg); str = get_arg_in_braces(str, line, TRUE); tokenize_script(root, lvl--, line); addtoken(root, lvl, TOKEN_TYPE_END, -1, "endparse"); break; case TOKEN_TYPE_REGEX: str = addregextoken(root, lvl, TOKEN_TYPE_REGEX, cmd, arg); // addtoken(root, --lvl, TOKEN_TYPE_END, -1, "endregex"); if (*str && *str != COMMAND_SEPARATOR) { addtoken(root, lvl++, TOKEN_TYPE_ELSE, -1, "else"); str = get_arg_in_braces(str, line, TRUE); tokenize_script(root, lvl--, line); addtoken(root, lvl, TOKEN_TYPE_END, -1, "endregex"); } break; case TOKEN_TYPE_RETURN: str = get_arg_with_spaces(arg, line, 1); addtoken(root, lvl, TOKEN_TYPE_RETURN, cmd, line); break; case TOKEN_TYPE_SWITCH: str = get_arg_in_braces(arg, line, FALSE); addtoken(root, lvl++, TOKEN_TYPE_SWITCH, cmd, line); str = get_arg_in_braces(str, line, TRUE); tokenize_script(root, lvl--, line); addtoken(root, lvl, TOKEN_TYPE_END, -1, "end"); break; case TOKEN_TYPE_WHILE: str = get_arg_in_braces(arg, line, FALSE); addtoken(root, lvl++, TOKEN_TYPE_WHILE, cmd, line); str = get_arg_in_braces(str, line, TRUE); tokenize_script(root, lvl--, line); addtoken(root, lvl, TOKEN_TYPE_END, -1, "endwhile"); break; default: str = get_arg_with_spaces(arg, line, 1); addtoken(root, lvl, TOKEN_TYPE_COMMAND, cmd, line); break; } } } if (*str == COMMAND_SEPARATOR) { str++; } } free(line); } struct scriptnode *parse_script(struct scriptroot *root, int lvl, struct scriptnode *token, struct scriptnode *shift) { struct scriptnode *split = NULL; while (token) { if (token->lvl < lvl) { if (shift->lvl + 1 == lvl) { switch (shift->type) { case TOKEN_TYPE_FOREACH: case TOKEN_TYPE_LOOP: case TOKEN_TYPE_PARSE: case TOKEN_TYPE_WHILE: debugtoken(root->ses, token); return shift; case TOKEN_TYPE_BROKEN_FOREACH: case TOKEN_TYPE_BROKEN_LOOP: case TOKEN_TYPE_BROKEN_PARSE: case TOKEN_TYPE_BROKEN_WHILE: shift->type--; return token; } } return token; } debugtoken(root->ses, token); switch (token->type) { case TOKEN_TYPE_BREAK: switch (shift->type) { case TOKEN_TYPE_FOREACH: breakforeachtoken(shift); shift->type++; break; case TOKEN_TYPE_LOOP: breaklooptoken(shift); shift->type++; break; case TOKEN_TYPE_PARSE: breakparsetoken(shift); shift->type++; break; case TOKEN_TYPE_WHILE: shift->type++; break; } do { token = token->next; } while (token && token->lvl > shift->lvl); continue; case TOKEN_TYPE_CASE: if (mathswitch(root->ses, shift->str, token->str)) { token = token->next; token = parse_script(root, lvl + 1, token, shift); while (token && token->lvl >= lvl) { token = token->next; } } else { do { token = token->next; } while (token && token->lvl > lvl); } continue; case TOKEN_TYPE_COMMAND: root->ses = (*command_table[token->cmd].command) (root->ses, token->str); break; case TOKEN_TYPE_CONTINUE: do { token = token->next; } while (token && token->lvl > shift->lvl); continue; case TOKEN_TYPE_DEFAULT: token = token->next; token = parse_script(root, lvl + 1, token, shift); while (token && token->lvl >= lvl) { token = token->next; } continue; case TOKEN_TYPE_ELSE: if (split) { token = parse_script(root, lvl + 1, token->next, shift); split = NULL; } else { do { token = token->next; } while (token && token->lvl > lvl); } continue; case TOKEN_TYPE_ELSEIF: if (split && get_number(root->ses, token->str)) { token = parse_script(root, lvl + 1, token->next, shift); split = NULL; } else { do { token = token->next; } while (token && token->lvl > lvl); } continue; case TOKEN_TYPE_END: break; case TOKEN_TYPE_FOREACH: if (*token->data->arg == 0) { resetforeachtoken(root->ses, token); } if (*token->data->arg == 0) { token->type++; do { token = token->next; } while (token && token->lvl > lvl); } else { set_nest_node(root->ses->list[LIST_VARIABLE], token->str, "%s", get_arg_foreach(token)); if (*token->data->arg == 0) { token->type++; } token = parse_script(root, lvl + 1, token->next, token); } continue; case TOKEN_TYPE_IF: split = NULL; if (get_number(root->ses, token->str)) { token = parse_script(root, lvl + 1, token->next, shift); } else { split = token; do { token = token->next; } while (token && token->lvl > lvl); } continue; case TOKEN_TYPE_LOOP: if (token->data->cnt == token->data->max + token->data->inc) { resetlooptoken(root->ses, token); } set_nest_node(root->ses->list[LIST_VARIABLE], token->str, "%lld", token->data->cnt); token->data->cnt += token->data->inc; if (token->data->cnt == token->data->max + token->data->inc) { token->type++; } token = parse_script(root, lvl + 1, token->next, token); continue; case TOKEN_TYPE_PARSE: if (*token->data->arg == 0) { resetparsetoken(root->ses, token); } set_nest_node(root->ses->list[LIST_VARIABLE], token->str, "%s", get_arg_parse(token)); if (*token->data->arg == 0) { token->type++; } token = parse_script(root, lvl + 1, token->next, token); continue; case TOKEN_TYPE_REGEX: split = NULL; token->regex->val = find(root->ses, token->str, token->regex->str, SUB_CMD); if (token->regex->val) { substitute(root->ses, token->regex->bod, token->regex->buf, SUB_CMD); root->ses = script_driver(root->ses, -1, token->regex->buf); } else { split = token; } break; case TOKEN_TYPE_RETURN: handlereturntoken(root->ses, token); if (lvl) { return NULL; } else { return (struct scriptnode *) root->ses; } break; case TOKEN_TYPE_SESSION: root->ses = parse_tintin_command(root->ses, token->str); break; case TOKEN_TYPE_STRING: root->ses = parse_input(root->ses, token->str); break; case TOKEN_TYPE_SWITCH: utime(); token = parse_script(root, lvl + 1, token->next, token); continue; case TOKEN_TYPE_WHILE: if (get_number(root->ses, token->str)) { token = parse_script(root, lvl + 1, token->next, token); } else { token->type++; do { token = token->next; } while (token && token->lvl > lvl); } continue; } if (token) { token = token->next; } } if (lvl) { return NULL; } else { return (struct scriptnode *) root->ses; } } char *write_script(struct session *ses, struct scriptroot *root) { struct scriptnode *token; static char buf[STRING_SIZE]; token = root->next; buf[0] = 0; while (token) { switch (token->type) { case TOKEN_TYPE_STRING: cat_sprintf(buf, "%s%s", indent(token->lvl), token->str); break; case TOKEN_TYPE_BREAK: case TOKEN_TYPE_CONTINUE: cat_sprintf(buf, "%s%c%s", indent(token->lvl), gtd->tintin_char, command_table[token->cmd].name); break; case TOKEN_TYPE_COMMAND: case TOKEN_TYPE_RETURN: cat_sprintf(buf, "%s%c%s%s%s", indent(token->lvl), gtd->tintin_char, command_table[token->cmd].name, *token->str ? " " : "", token->str); break; case TOKEN_TYPE_ELSE: cat_sprintf(buf, "%s%c%s\n%s{\n", indent(token->lvl), gtd->tintin_char, token->str, indent(token->lvl)); break; case TOKEN_TYPE_DEFAULT: cat_sprintf(buf, "%s%c%s\n%s{\n", indent(token->lvl), gtd->tintin_char, command_table[token->cmd].name, indent(token->lvl)); break; case TOKEN_TYPE_FOREACH: case TOKEN_TYPE_LOOP: case TOKEN_TYPE_PARSE: cat_sprintf(buf, "%s%c%s %s\n%s{\n", indent(token->lvl), gtd->tintin_char, command_table[token->cmd].name, token->data->cpy, indent(token->lvl)); break; case TOKEN_TYPE_CASE: case TOKEN_TYPE_ELSEIF: case TOKEN_TYPE_IF: case TOKEN_TYPE_SWITCH: case TOKEN_TYPE_WHILE: cat_sprintf(buf, "%s%c%s {%s}\n%s{\n", indent(token->lvl), gtd->tintin_char, command_table[token->cmd].name, token->str, indent(token->lvl)); break; case TOKEN_TYPE_REGEX: cat_sprintf(buf, "%s%c%s {%s} {%s} {%s}", indent(token->lvl), gtd->tintin_char, command_table[token->cmd].name, token->str, token->regex->str, token->regex->bod); break; case TOKEN_TYPE_END: cat_sprintf(buf, "\n%s}", indent(token->lvl)); break; case TOKEN_TYPE_SESSION: cat_sprintf(buf, "%s%c%s", indent(token->lvl), gtd->tintin_char, token->str); break; default: tintin_printf2(ses, "#WRITE: UNKNOWN TOKEN TYPE: %d", token->type); break; } if (token->next && token->lvl == token->next->lvl) { strcat(buf, ";\n"); } token = token->next; } while (root->next) { deltoken(root, root->next); } free(root); return buf; } struct session *script_driver(struct session *ses, int list, char *str) { struct scriptroot *root; struct session *cur_ses; int dlevel, ilevel; root = (struct scriptroot *) calloc(1, sizeof(struct scriptroot)); root->ses = cur_ses = ses; dlevel = (list >= 0) ? HAS_BIT(root->ses->list[list]->flags, LIST_FLAG_DEBUG) : 0; ilevel = (list >= 0) ? 1 : 0; cur_ses->debug_level += dlevel; cur_ses->input_level += ilevel; tokenize_script(root, 0, str); ses = (struct session *) parse_script(root, 0, root->next, root->prev); cur_ses->debug_level -= dlevel; cur_ses->input_level -= ilevel; while (root->prev) { deltoken(root, root->prev); } free(root); return ses; } char *script_writer(struct session *ses, char *str) { struct scriptroot *root; root = (struct scriptroot *) calloc(1, sizeof(struct scriptroot)); root->ses = ses; tokenize_script(root, 1, str); return write_script(ses, root); }