/****************************************************************************** * This file is part of TinTin++ * * * * Copyright 2004-2019 Igor van den Hoven * * * * TinTin++ 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 3 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 TinTin++. If not, see https://www.gnu.org/licenses. * ******************************************************************************/ /****************************************************************************** * (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" #include DO_COMMAND(do_read) { FILE *file; arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN); arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN); if ((file = fopen(arg1, "r")) == NULL) { check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "READ ERROR", arg1, "FILE NOT FOUND."); tintin_printf(ses, "#READ {%s}: FILE NOT FOUND.", arg1); return ses; } ses = read_file(ses, file, arg1); fclose(file); return ses; } struct session *read_file(struct session *ses, FILE *file, char *filename) { char *bufi, *bufo, temp[INPUT_SIZE], *pti, *pto, last = 0; int lvl, cnt, com, lnc, fix, ok, verbose, size; int counter[LIST_MAX]; temp[0] = getc(file); if (!ispunct((int) temp[0])) { check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "READ ERROR", filename, "INVALID START OF FILE"); tintin_printf(ses, "#ERROR: #READ {%s}: INVALID START OF FILE '%c'.", filename, temp[0]); return ses; } ungetc(temp[0], file); for (cnt = 0 ; cnt < LIST_MAX ; cnt++) { if (HAS_BIT(list_table[cnt].flags, LIST_FLAG_READ)) { counter[cnt] = ses->list[cnt]->used; } } fseek(file, 0, SEEK_END); size = ftell(file); fseek(file, 0, SEEK_SET); if ((bufi = (char *) calloc(1, size + 2)) == NULL || (bufo = (char *) calloc(1, size + 2)) == NULL) { check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "READ ERROR", filename, "FAILED TO ALLOCATE MEMORY"); tintin_printf(ses, "#ERROR: #READ {%s}: FAILED TO ALLOCATE %d BYTES OF MEMORY.", filename, size + 2); return ses; } if (fread(bufi, 1, size, file) <= 0) { check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "READ ERROR", filename, "FREAD FAILURE"); tintin_printf(ses, "#ERROR: #READ {%s}: FREAD FAILURE.", filename); return ses; } pti = bufi; pto = bufo; lvl = com = lnc = fix = ok = 0; while (*pti) { if (com == 0) { if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti)) { *pto++ = *pti++; *pto++ = *pti++; continue; } switch (*pti) { case DEFAULT_OPEN: *pto++ = *pti++; lvl++; last = DEFAULT_OPEN; break; case DEFAULT_CLOSE: *pto++ = *pti++; lvl--; last = DEFAULT_CLOSE; break; case COMMAND_SEPARATOR: *pto++ = *pti++; last = COMMAND_SEPARATOR; break; case ' ': *pto++ = *pti++; break; case '/': if (lvl == 0 && pti[1] == '*') { pti += 2; com += 1; } else { *pto++ = *pti++; } break; case '\t': *pto++ = *pti++; break; case '\r': pti++; break; case '\n': lnc++; pto--; while (is_space(*pto)) { pto--; } pto++; if (fix == 0 && pti[1] == gtd->tintin_char) { if (lvl == 0) { ok = lnc + 1; } else { fix = lnc; } } if (lvl) { pti++; while (is_space(*pti)) { if (*pti == '\n') { lnc++; if (fix == 0 && pti[1] == gtd->tintin_char) { fix = lnc; } } pti++; } if (last != COMMAND_SEPARATOR && last != DEFAULT_OPEN) { if (*pti == gtd->tintin_char) { show_error(ses, LIST_COMMAND, "#WARNING: #READ {%s}: MISSING SEMICOLON ON LINE %d.", filename, lnc); } } if (*pti != DEFAULT_CLOSE && last == 0) { *pto++ = ' '; } } else for (cnt = 1 ; ; cnt++) { if (pti[cnt] == 0) { *pto++ = *pti++; break; } if (pti[cnt] == DEFAULT_OPEN) { pti++; while (is_space(*pti)) { pti++; } *pto++ = ' '; break; } if (!is_space(pti[cnt])) { *pto++ = *pti++; break; } } break; default: *pto++ = *pti++; last = 0; break; } } else { switch (*pti) { case '/': if (pti[1] == '*') { pti += 2; com += 1; } else { pti += 1; } break; case '*': if (pti[1] == '/') { pti += 2; com -= 1; } else { pti += 1; } break; case '\n': lnc++; pti++; break; default: pti++; break; } } } *pto++ = '\n'; *pto = '\0'; if (lvl) { check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "READ ERROR", filename, "MISSING BRACE OPEN OR CLOSE"); tintin_printf(ses, "#ERROR: #READ {%s}: MISSING %d '%c' BETWEEN LINE %d AND %d.", filename, abs(lvl), lvl < 0 ? DEFAULT_OPEN : DEFAULT_CLOSE, fix == 0 ? 1 : ok, fix == 0 ? lnc + 1 : fix); free(bufi); free(bufo); return ses; } if (com) { check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "READ ERROR", filename, "MISSING COMMENT OPEN OR CLOSE"); tintin_printf(ses, "#ERROR: #READ {%s}: MISSING %d '%s'", filename, abs(com), com < 0 ? "/*" : "*/"); free(bufi); free(bufo); return ses; } sprintf(temp, "#CONFIG {TINTIN CHAR} {%c}", bufo[0]); if (bufo[0] != gtd->tintin_char) { gtd->level->verbose++; gtd->level->debug++; show_error(ses, LIST_COMMAND, "#WARNING: #READ {%s}: SETTING THE COMMAND CHARACTER TO '%c' BECAUSE IT'S THE FIRST CHARACTER IN THE FILE.", filename, bufo[0]); gtd->level->debug--; gtd->level->verbose--; gtd->level->quiet++; script_driver(ses, LIST_COMMAND, NULL, temp); gtd->level->quiet--; } verbose = HAS_BIT(ses->config_flags, CONFIG_FLAG_VERBOSE) ? 1 : 0; gtd->level->input++; gtd->level->verbose += verbose; lvl = 0; lnc = 0; pti = bufo; pto = bufi; while (*pti) { if (*pti != '\n') { *pto++ = *pti++; continue; } lnc++; *pto = 0; if (pto - bufi >= BUFFER_SIZE) { show_debug(ses, LIST_COMMAND, NULL, "#WARNING: #READ {%s}: POSSIBLE BUFFER OVERFLOW AT COMMAND: %.30s", filename, bufi); } if (bufi[0]) { ses = script_driver(ses, LIST_COMMAND, NULL, bufi); } pto = bufi; pti++; } gtd->level->verbose -= verbose; gtd->level->input--; if (!HAS_BIT(ses->config_flags, CONFIG_FLAG_VERBOSE)) { for (cnt = 0 ; cnt < LIST_MAX ; cnt++) { if (HAS_BIT(list_table[cnt].flags, LIST_FLAG_READ)) { switch (ses->list[cnt]->used - counter[cnt]) { case 0: break; case 1: show_message(ses, cnt, "#OK: %3d %s LOADED.", ses->list[cnt]->used - counter[cnt], list_table[cnt].name); break; default: show_message(ses, cnt, "#OK: %3d %s LOADED.", ses->list[cnt]->used - counter[cnt], list_table[cnt].name_multi); break; } } } } free(bufi); free(bufo); check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 1, "READ FILE", filename); check_all_events(ses, EVENT_FLAG_SYSTEM, 1, 1, "READ FILE %s", filename, filename); return ses; } DO_COMMAND(do_write) { FILE *file; struct listroot *root; struct listnode *node; int i, j, fix, cnt = 0; arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN); arg = get_arg_in_braces(ses, arg, arg2, GET_ONE); if (*arg1 == 0) { check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "WRITE ERROR", arg1, "INVALID FILE NAME"); show_error(ses, LIST_COMMAND, "#SYNTAX: #WRITE [FORCE]"); return ses; } if (is_suffix(arg1, ".map") && !is_abbrev(arg2, "FORCE")) { check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "WRITE ERROR", arg1, "INVALID FILE EXTENSION"); tintin_printf2(ses, "#WRITE {%s}: USE {FORCE} TO OVERWRITE .map FILES.", arg1); return ses; } if ((file = fopen(arg1, "w")) == NULL) { check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "WRITE ERROR", arg1, "FAILED TO OPEN"); show_error(ses, LIST_COMMAND, "#ERROR: #WRITE: COULDN'T OPEN {%s} TO WRITE.", arg1); return ses; } for (i = 0 ; i < LIST_MAX ; i++) { root = ses->list[i]; if (!HAS_BIT(root->flags, LIST_FLAG_WRITE)) { continue; } fix = 0; for (j = 0 ; j < root->used ; j++) { node = root->list[j]; if (node->shots) { continue; } if (*node->group == 0) { write_node(ses, i, node, file); cnt++; fix++; } } if (fix) { fputs("\n", file); } } fclose(file); check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 1, "WRITE FILE", arg1); show_message(ses, LIST_COMMAND, "#WRITE: %d COMMANDS WRITTEN TO {%s}.", cnt, arg1); return ses; } void write_node(struct session *ses, int list, struct listnode *node, FILE *file) { char *result, *str; int val = 0; push_call("write_node(%d,%p,%p)",list,node,file); switch (list) { case LIST_EVENT: case LIST_FUNCTION: case LIST_MACRO: val = asprintf(&result, "%c%s {%s}\n{\n%s\n}\n\n", gtd->tintin_char, list_table[list].name, node->arg1, script_writer(ses, node->arg2)); break; case LIST_ACTION: case LIST_ALIAS: case LIST_BUTTON: if (!strcmp(node->arg3, "5")) { val = asprintf(&result, "%c%s {%s}\n{\n%s\n}\n\n", gtd->tintin_char, list_table[list].name, node->arg1, script_writer(ses, node->arg2)); } else { val = asprintf(&result, "%c%s {%s}\n{\n%s\n}\n{%s}\n\n", gtd->tintin_char, list_table[list].name, node->arg1, script_writer(ses, node->arg2), node->arg3); } break; case LIST_VARIABLE: str = str_alloc_stack(0); // show_nest_node(node, &str, 1); view_nest_node(node, &str, 0, TRUE, FALSE); if (node->root) { val = asprintf(&result, "%c%s {%s}\n{\n%s}\n", gtd->tintin_char, list_table[list].name, node->arg1, str); } else { val = asprintf(&result, "%c%s {%s} {%s}\n", gtd->tintin_char, list_table[list].name, node->arg1, str); } break; default: switch (list_table[list].args) { case 0: result = strdup(""); break; case 1: val = asprintf(&result, "%c%s {%s}\n", gtd->tintin_char, list_table[list].name, node->arg1); break; case 2: val = asprintf(&result, "%c%s {%s} {%s}\n", gtd->tintin_char, list_table[list].name, node->arg1, node->arg2); break; case 3: val = asprintf(&result, "%c%s {%s} {%s} {%s}\n", gtd->tintin_char, list_table[list].name, node->arg1, node->arg2, node->arg3); break; case 4: val = asprintf(&result, "%c%s {%s} {%s} {%s} {%s}\n", gtd->tintin_char, list_table[list].name, node->arg1, node->arg2, node->arg3, node->arg4); break; } break; } if (val != -1) { fputs(result, file); free(result); } else { syserr_printf(ses, "write_node: asprintf:"); } pop_call(); return; } char *fread_one_line(char **str, FILE *file) { int byte; str_cpy(str, ""); while (TRUE) { byte = getc(file); switch (byte) { case ASCII_LF: return *str; case EOF: if (*(*str)) { return *str; } return NULL; case ASCII_NUL: tintin_printf2(gtd->ses, "fread_one_line: encountered NULL character."); break; case ASCII_CR: break; case ASCII_HTAB: str_cat_printf(str, "%*s", gtd->ses->tab_width, ""); break; default: str_cat_chr(str, byte); break; } } return *str; }