diff options
Diffstat (limited to 'engines/glk/jacl/interpreter.cpp')
-rw-r--r-- | engines/glk/jacl/interpreter.cpp | 3458 |
1 files changed, 3458 insertions, 0 deletions
diff --git a/engines/glk/jacl/interpreter.cpp b/engines/glk/jacl/interpreter.cpp new file mode 100644 index 0000000000..47c07c8acd --- /dev/null +++ b/engines/glk/jacl/interpreter.cpp @@ -0,0 +1,3458 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "glk/jacl/jacl.h" +#include "glk/jacl/language.h" +#include "glk/jacl/types.h" +#include "glk/jacl/prototypes.h" +#include "glk/jacl/csv.h" +#include "common/str.h" + +namespace Glk { +namespace JACL { + +#ifdef WIN32 +struct flock { + short l_type; + short l_whence; + long l_start; + long l_len; + long l_pid; +}; + +#define F_DUPFD 0 +#define F_GETFD 1 +#define F_SETFD 2 +#define F_GETFL 3 +#define F_SETFL 4 +#define F_GETLK 5 +#define F_SETLK 6 +#define F_SETLKW 7 + +#define F_RDLCK 0 +#define F_WRLCK 1 +#define F_UNLCK 2 + +int fcntl(int __fd, int __cmd, ...) { + return 0; +} +#endif /* WIN32 */ + +#ifndef strcasestr +char *strcasestr(const char *s, const char *find) { + char c, sc; + size_t len; + + if ((c = *find++) != 0) { + c = (char)tolower((unsigned char)c); + len = strlen(find); + do { + do { + if ((sc = *s++) == 0) + return (NULL); + } while ((char)tolower((unsigned char)sc) != c); + } while (scumm_strnicmp(s, find, len) != 0); + s--; + } + return ((char *)s); +} +#endif + +#define MAX_TRY 10 + +struct flock read_lck; +int read_fd; +struct flock write_lck; +int write_fd; + +char *url_encode(char *str); +char to_hex(char code); + +char *location_attributes[] = { + "VISITED ", "DARK ", "ON_WATER ", "UNDER_WATER ", "WITHOUT_AIR ", "OUTDOORS ", + "MID_AIR ", "TIGHT_ROPE ", "POLLUTED ", "SOLVED ", "MID_WATER ", "DARKNESS ", + "MAPPED ", "KNOWN ", + NULL +}; + +char *object_attributes[] = { + "CLOSED ", "LOCKED ", "DEAD ", "IGNITABLE ", "WORN ", "CONCEALING ", + "LUMINOUS ", "WEARABLE ", "CLOSABLE ", "LOCKABLE ", "ANIMATE ", "LIQUID ", + "CONTAINER ", "SURFACE ", "PLURAL ", "FLAMMABLE ", "BURNING ", "LOCATION ", + "ON ", "DAMAGED ", "FEMALE ", "POSSESSIVE ", "OUT_OF_REACH ", "TOUCHED ", + "SCORED ", "SITTING ", "NPC ", "DONE ", "GAS ", "NO_TAB ", + "NOT_IMPORTANT ", NULL +}; + +char *object_elements[] = { + "parent", "capacity", "mass", "bearing", "velocity", "next", "previous", + "child", "index", "status", "state", "counter", "points", "class", "x", "y", + NULL +}; + +char *location_elements[] = { + "north", "south", "east", "west", "northeast", "northwest", "southeast", + "southwest", "up", "down", "in", "out", "points", "class", "x", "y", + NULL +}; + +struct csv_parser parser_csv; +char in_name[1024]; +char out_name[1024]; +Common::SeekableReadStream *infile; +Common::WriteStream *outfile; + +int stack = 0; +int proxy_stack = 0; + +int field_no = 0; + +struct stack_type backup[STACK_SIZE]; +struct proxy_type proxy_backup[STACK_SIZE]; + +struct function_type *resolved_function = NULL; +struct string_type *resolved_string = NULL; + +struct string_type *new_string = NULL; +struct string_type *current_cstring = NULL; +struct string_type *previous_cstring = NULL; + +struct cinteger_type *new_cinteger = NULL; +struct cinteger_type *current_cinteger = NULL; +struct cinteger_type *previous_cinteger = NULL; + +long bit_mask; +extern int encrypted; +extern int after_from; +extern int last_exact; + +extern char temp_directory[]; +extern char data_directory[]; +char csv_buffer[1024]; + +int resolved_attribute; + +/* THE ITERATION VARIABLE USED FOR LOOPS */ +int *loop_integer = NULL; +int *select_integer = NULL; + +int criterion_value = 0; +int criterion_type = 0; +int criterion_negate = FALSE; +int current_level; +int execution_level; +int *ask_integer; +int new_x; +int new_y; + +int interrupted = FALSE; +char string_buffer[2048]; +char argument_buffer[1024]; +#ifdef GLK +extern schanid_t sound_channel[]; +extern strid_t game_stream; +extern winid_t mainwin; +extern winid_t statuswin; +extern winid_t current_window; + +extern strid_t mainstr; +extern strid_t statusstr; +extern strid_t quotestr; +extern strid_t inputstr; +int top_of_loop = 0; +int top_of_select = 0; +int top_of_while = 0; +int top_of_iterate = 0; +int top_of_update = 0; +int top_of_do_loop = 0; +#else +extern FILE *file; +char option_buffer[2024]; +int style_stack[100]; +int style_index = 0; +long top_of_loop = 0; +long top_of_select = 0; +long top_of_while = 0; +long top_of_iterate = 0; +long top_of_update = 0; +long top_of_do_loop = 0; + +#endif + +#ifdef __NDS__ +extern int bold_mode; +extern int pre_mode; +extern int reverse_mode; +extern int input_mode; +extern int subheader_mode; +extern int note_mode; +#endif + +extern char user_id[]; +extern char prefix[]; +extern char text_buffer[]; +extern char chunk_buffer[]; +extern char *word[]; + +extern char bookmark[]; +extern char file_prompt[]; + +/* CONTAINED IN PARSER.C */ +extern int object_list[4][MAX_WORDS]; +extern int list_size[]; +extern int max_size[]; + +/* CONTAINED IN ENCAPSULATE.C */ +extern int quoted[]; + +extern struct object_type *object[]; +extern struct integer_type *integer_table; +extern struct integer_type *integer[]; +extern struct cinteger_type *cinteger_table; +extern struct attribute_type *attribute_table; +extern struct string_type *string_table; +extern struct string_type *cstring_table; +extern struct function_type *function_table; +extern struct function_type *executing_function; +extern struct command_type *completion_list; +extern struct word_type *grammar_table; +extern struct synonym_type *synonym_table; +extern struct filter_type *filter_table; + +extern char function_name[]; +extern char temp_buffer[]; +extern char error_buffer[]; +extern char proxy_buffer[]; + +extern char default_function[]; +extern char override[]; + +extern int noun[]; +extern int wp; +extern int start_of_this_command; +extern int start_of_last_command; +extern int buffer_index; +extern int objects; +extern int integers; +extern int player; +extern int oec; +extern int *object_element_address; +extern int *object_backup_address; +extern int walkthru_running; + +// VALUES FROM LOADER +extern int value_resolved; + +extern Common::WriteStream *transcript; +extern char margin_string[]; + +char integer_buffer[16]; +char called_name[1024]; +char scope_criterion[24]; +char *output; + +void terminate(int code) { + // FREE ANY EXTRA RAM ALLOCATED BY THE CSV PARSER + csv_free(&parser_csv); + +#ifdef GLK + int index; + event_t event; + + // FLUSH THE GLK WINDOW SO THE ERROR GETS DISPLAYED IMMEDIATELY. + g_vm->glk_select_poll(&event); + + /* CLOSE THE SOUND CHANNELS */ + for (index = 0; index < 8; index++) { + if (sound_channel[index] != NULL) { + g_vm->glk_schannel_destroy(sound_channel[index]); + } + } + + /* CLOSE THE STREAM */ + if (game_stream != NULL) { + g_vm->glk_stream_close(game_stream, NULL); + } + + g_vm->glk_exit(); +#else + if (file != NULL) /* CLOSE THE GAME FILE */ + fclose(file); + + exit(code); +#endif +} + +void build_proxy() { + int index; + + proxy_buffer[0] = 0; + + /* LOOP THROUGH ALL THE PARAMETERS OF THE PROXY COMMAND + AND BUILD THE MOVE TO BE ISSUED ON THE PLAYER'S BEHALF */ + for (index = 1; word[index] != NULL; index++) { + strcat(proxy_buffer, text_of_word(index)); + } + + for (index = 0; index < (int)strlen(proxy_buffer); index++) { + if (proxy_buffer[index] == '~') { + proxy_buffer[index] = '\"'; + } + } + + //printf("--- proxy buffer = \"%s\"\n", proxy_buffer); +} + +void cb1(void *s, size_t i, void *not_used) { + struct string_type *resolved_cstring; + + //sprintf (temp_buffer, "Trying to set field %d to equal %s^", field_no, (char *) s); + //write_text(temp_buffer); + + sprintf(temp_buffer, "field[%d]", field_no); + + if ((resolved_cstring = cstring_resolve(temp_buffer)) != NULL) { + //write_text("Resolved "); + //write_text(temp_buffer); + //write_text("^"); + strncpy(resolved_cstring->value, (const char *)s, i); + resolved_cstring->value[i] = 0; + //sprintf(temp_buffer, "Setting field %d to ~%s~^", field_no, (char *) s); + //write_text(temp_buffer); + // INCREMENT THE FIELD NUMBER SO THE NEXT ONE GETS STORED IN THE RIGHT CONSTANT + field_no++; + } else { + write_text("Can't resolve "); + write_text(temp_buffer); + write_text("^"); + } + +} + +void cb2(int c, void *not_used) { + // THE END OF THE RECORD HAS BEEN REACHED, EXPORT THE NUMBER OF FIELDS READ + struct cinteger_type *resolved_cinteger; + + if ((resolved_cinteger = cinteger_resolve("field_count")) != NULL) { + resolved_cinteger->value = field_no; + } +} + +int execute(char *funcname) { + int index; + int counter; + int *container; + + int object_1, + object_2; + + /* THESE VARIABLE KEEP TRACK OF if AND endif COMMANDS TO DECIDE WHETHER + *THE CURRENT LINE OF CODE SHOULD BE EXECUTED OR NOT */ + int currentLevel = 0; + int executionLevel = 0; + + /* THESE ARE USED AS FILE POINTER OFFSETS TO RETURN TO FIXED + * POINTS IN THE GAME FILE */ +#ifdef GLK + int result; + int before_command = 0; +#else + long before_command = 0; +#endif + + + strncpy(called_name, funcname, 1023); + + /* GET THE FUNCTION OBJECT BY THE FUNCTION NAME */ + resolved_function = function_resolve(called_name); + + if (resolved_function == NULL) { + //printf("--- failed to find %s\n", called_name); + return (FALSE); + } + +#ifdef GLK + push_stack(g_vm->glk_stream_get_position(game_stream)); +#else + push_stack(ftell(file)); +#endif + + top_of_loop = 0; + top_of_select = 0; + top_of_while = 0; + top_of_iterate = 0; + top_of_update = 0; + top_of_do_loop = 0; + + executing_function = resolved_function; + executing_function->call_count++; + + // CREATE ALL THE PASSED ARGUMENTS AS JACL INTEGER CONSTANTS + set_arguments(called_name); + + // SET function_name TO THE CORE NAME STORED IN THE FUNCTION OBJECT + // LEAVING called_name TO CONTAIN THE FULL ARGUMENT LIST + strncpy(function_name, executing_function->name, 80); + strncpy(cstring_resolve("function_name")->value, executing_function->name, 80); + + //sprintf(temp_buffer, "--- starting to execute %s^", function_name); + //write_text(temp_buffer); + + // JUMP TO THE POINT IN THE PROCESSED GAME FILE WHERE THIS FUNCTION STARTS +#ifdef GLK + g_vm->glk_stream_set_position(game_stream, executing_function->position, seekmode_Start); + before_command = executing_function->position; + result = glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024); +#else + fseek(file, executing_function->position, SEEK_SET); + before_command = executing_function->position; + fgets(text_buffer, 1024, file); +#endif + + if (encrypted) jacl_decrypt(text_buffer); + + while (text_buffer[0] != 125 && !interrupted) { + encapsulate(); + if (word[0] == NULL); + else if (!strcmp(word[0], "endwhile")) { + currentLevel--; + if (currentLevel < executionLevel) { + // THIS ENDWHILE COMMAND WAS BEING EXECUTED, + // NOT JUST COUNTED. + if (top_of_while == FALSE) { + sprintf(error_buffer, NO_WHILE, executing_function->name); + log_error(error_buffer, PLUS_STDOUT); + } else { +#ifdef GLK + g_vm->glk_stream_set_position(game_stream, top_of_while, seekmode_Start); +#else + fseek(file, top_of_while, SEEK_SET); +#endif + executionLevel = currentLevel; + } + } + } else if (!strcmp(word[0], "enditerate")) { + currentLevel--; + if (currentLevel < executionLevel) { + // THIS ENDITERATE COMMAND WAS BEING EXECUTED, + // NOT JUST COUNTED. + if (top_of_iterate == FALSE) { + sprintf(error_buffer, NO_ITERATE, executing_function->name); + log_error(error_buffer, PLUS_STDOUT); + } else { +#ifdef GLK + g_vm->glk_stream_set_position(game_stream, top_of_iterate, seekmode_Start); +#else + fseek(file, top_of_iterate, SEEK_SET); +#endif + executionLevel = currentLevel; + } + } + } else if (!strcmp(word[0], "endupdate")) { + currentLevel--; + if (currentLevel < executionLevel) { + // THIS ENDUPDATE COMMAND WAS BEING EXECUTED, + // NOT JUST COUNTED. + if (top_of_update == FALSE) { + sprintf(error_buffer, NO_UPDATE, executing_function->name); + log_error(error_buffer, PLUS_STDOUT); + } else { +#ifdef GLK + g_vm->glk_stream_set_position(game_stream, top_of_update, seekmode_Start); +#else + fseek(file, top_of_update, SEEK_SET); +#endif + executionLevel = currentLevel; + } + } + } else if (!strcmp(word[0], "print") && currentLevel != executionLevel) { + // SKIP THIS BLOCK OF PLAIN TEXT UNTIL IT FINDS A + // LINE THAT STARTS WITH A '.' OR A '}' +#ifdef GLK + glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024); +#else + fgets(text_buffer, 1024, file); +#endif + + if (encrypted) jacl_decrypt(text_buffer); + + while (text_buffer[0] != '.') { + if (text_buffer[0] == '}') { + // HIT THE END OF THE FUNCTION, JUST BAIL OUT + return (exit_function(TRUE)); + } + + // GET THE NEXT LINE +#ifdef GLK + glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024); +#else + fgets(text_buffer, 1024, file); +#endif + + if (encrypted) jacl_decrypt(text_buffer); + + } + } else if (!strcmp(word[0], "endif")) { + currentLevel--; + if (currentLevel < executionLevel) { + /* THIS SHOULD NEVER HAPPEN */ + executionLevel = currentLevel; + } + } else if (!strcmp(word[0], "endall")) { + currentLevel = 0; + executionLevel = 0; + } else if (!strcmp(word[0], "else")) { + if (currentLevel == executionLevel) { + executionLevel--; + } else if (currentLevel == executionLevel + 1) { + executionLevel++; + } + } else if (currentLevel == executionLevel) { + if (!strcmp(word[0], "look")) { + // THIS IS JUST HERE FOR BACKWARDS COMPATIBILITY + object[HERE]->attributes &= ~1L; + look_around(); + } else if (!strcmp(word[0], "repeat")) { +#ifdef GLK + top_of_do_loop = g_vm->glk_stream_get_position(game_stream); +#else + top_of_do_loop = ftell(file); +#endif + } else if (!strcmp(word[0], "until")) { + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + if (top_of_do_loop == FALSE) { + sprintf(error_buffer, NO_REPEAT, executing_function->name); + log_error(error_buffer, PLUS_STDOUT); + } else if (!condition()) { +#ifdef GLK + g_vm->glk_stream_set_position(game_stream, top_of_do_loop, seekmode_Start); +#else + fseek(file, top_of_do_loop, SEEK_SET); +#endif + } + } + } else if (!strcmp(word[0], "untilall")) { + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + if (top_of_do_loop == FALSE) { + sprintf(error_buffer, NO_REPEAT, executing_function->name); + log_error(error_buffer, PLUS_STDOUT); + } else if (!and_condition()) { +#ifdef GLK + g_vm->glk_stream_set_position(game_stream, top_of_do_loop, seekmode_Start); +#else + fseek(file, top_of_do_loop, SEEK_SET); +#endif + + } + } + } else if (!strcmp(word[0], "iterate")) { + int i; + + // A NEW iterate LOOP MEANS STARTING BACK AT THE FIRST FIELD + field_no = 0; + + currentLevel++; + /* THIS LOOP COMES BACK TO THE START OF THE LINE CURRENTLY + EXECUTING, NOT THE LINE AFTER */ + + top_of_iterate = before_command; + + // infile REMAINS OPEN DURING THE ITERATION, ONLY NEEDS + // OPENING THE FIRST TIME + if (infile == NULL) { + strcpy(temp_buffer, data_directory); + strcat(temp_buffer, prefix); + strcat(temp_buffer, "-"); + strcat(temp_buffer, text_of_word(1)); + strcat(temp_buffer, ".csv"); + + infile = File::openForReading(temp_buffer); + + if (word[2] != NULL && !strcmp(word[2], "skip_header")) { + assert(infile); + infile->read(csv_buffer, 1024); + } + } + + if (infile == NULL) { + sprintf(error_buffer, "Failed to open file %s: %s\n", temp_buffer, strerror(errno)); + log_error(error_buffer, LOG_ONLY); + infile = NULL; + } else { + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + // IF THERE IS ANOTHER RECORD TO READ FROM THE CSV FILE THEN + // SET THE field[] CONSTANTS AND INCREMENT THE executionLevel + infile->read(csv_buffer, 1024); + + if (infile->pos() < infile->size()) { + i = strlen(csv_buffer); + //sprintf (temp_buffer, "Read ~%s~ with %d bytes.^", csv_buffer, i); + //write_text(temp_buffer); + if (csv_parse(&parser_csv, csv_buffer, i, cb1, cb2, (void *) NULL) != (uint)i) { + sprintf(error_buffer, "Error parsing file: %s\n", csv_strerror(csv_error(&parser_csv))); + log_error(error_buffer, PLUS_STDOUT); + delete infile; + infile = NULL; + } else { + // A LINE HAS BEEN SUCCESSFULLY READ, EXECUTE THE CONTENTS OF THE LOOP + executionLevel++; + } + } else { + delete infile; + infile = NULL; + } + } + } + } else if (!strcmp(word[0], "update")) { + int i; + + // SET UP THE RECORD LOCKING STRUCTURE, THE ADDRESS OF WHICH + // IS PASSED TO THE fcntl() SYSTEM CALL + write_lck.l_type = F_WRLCK; // SETTING A WRITE LOCK + write_lck.l_whence = 0; // OFFSET l_start FROM BEGINNING OF FILE + write_lck.l_start = 0LL; + write_lck.l_len = 0LL; // UNTIL THE END OF THE FILE ADDRESS SPACE + + read_lck.l_type = F_RDLCK; // SETTING A READ LOCK + read_lck.l_whence = 0; // OFFSET l_start FROM BEGINNING OF FILE + read_lck.l_start = 0LL; + read_lck.l_len = 0LL; // UNTIL THE END OF THE FILE ADDRESS SPACE + + // A NEW iterate LOOP MEANS STARTING BACK AT THE FIRST FIELD + field_no = 0; + + currentLevel++; + // THIS LOOP COMES BACK TO THE START OF THE LINE CURRENTLY + // EXECUTING, NOT THE LINE AFTER + + top_of_update = before_command; + + // infile REMAINS OPEN DURING THE ITERATION, ONLY NEEDS + // OPENING THE FIRST TIME + if (infile == NULL) { + strcpy(in_name, data_directory); + strcat(in_name, prefix); + strcat(in_name, "-"); + strcat(in_name, text_of_word(1)); + strcat(in_name, ".csv"); + + infile = File::openForReading(in_name); + } + + if (outfile == NULL) { + // OPEN A TEMPORARY OUTPUT FILE TO WRITE THE MODIFICATIONS TO + strcpy(out_name, data_directory); + strcat(out_name, prefix); + strcat(out_name, "-"); + strcat(out_name, text_of_word(1)); + strcat(out_name, "-"); + strcat(out_name, user_id); + strcat(out_name, ".csv"); + + outfile = File::openForWriting(out_name); + } + + if (infile == NULL) { + sprintf(error_buffer, "Failed to open input CSV file ~%s~: %s\n", in_name, strerror(errno)); + log_error(error_buffer, LOG_ONLY); + if (outfile != NULL) { + delete outfile; + outfile = NULL; + } + return (exit_function(TRUE)); + } else { + if (outfile == NULL) { + sprintf(error_buffer, "Failed to open output CSV file ~%s~: %s\n", out_name, strerror(errno)); + log_error(error_buffer, LOG_ONLY); + if (infile != NULL) { + delete infile; + infile = NULL; + } + return (exit_function(TRUE)); + } else { +#ifdef FILE_CTL + int tryCtr = 0; + write_fd = fileno(outfile); + // ATTEMPT LOCKING OUTPUT FILE MAX_TRY TIMES BEFORE GIVING UP. + while (fcntl(write_fd, F_SETLK, &write_lck) < 0) { + if (errno == EAGAIN || errno == EACCES) { + // THERE MIGHT BE OTHER ERROR CASES IN WHICH + // USERS MIGHT TRY AGAIN + if (++tryCtr < MAX_TRY) { + jacl_sleep(1000); + continue; + } + sprintf(error_buffer, "File busy unable to get lock on output file.\n"); + log_error(error_buffer, PLUS_STDOUT); + return (exit_function(TRUE)); + } + } + + tryCtr = 0; + + read_fd = fileno(infile); + // ATTEMPT LOCKING OUTPUT FILE MAX_TRY TIMES BEFORE GIVING UP. + while (fcntl(read_fd, F_SETLK, &read_lck) < 0) { + if (errno == EAGAIN || errno == EACCES) { + // THERE MIGHT BE OTHER ERROR CASES IN WHICH + // USERS MIGHT TRY AGAIN + if (++tryCtr < MAX_TRY) { + jacl_sleep(1000); + continue; + } + sprintf(error_buffer, "File busy unable to get lock on input file.\n"); + log_error(error_buffer, PLUS_STDOUT); + return (exit_function(TRUE)); + } + } +#endif + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + // IF THERE IS ANOTHER RECORD TO READ FROM THE CSV FILE THEN + // SET THE field[] CONSTANTS AND INCREMENT THE executionLevel + infile->read(csv_buffer, 1024); + if (infile->pos() < infile->size()) { + i = strlen(csv_buffer); + if (csv_parse(&parser_csv, csv_buffer, i, cb1, cb2, (int *) &field_no) != (uint)i) { + sprintf(error_buffer, "Error parsing file: %s\n", csv_strerror(csv_error(&parser_csv))); + log_error(error_buffer, PLUS_STDOUT); + read_lck.l_type = F_UNLCK; // SETTING A READ LOCK + fcntl(read_fd, F_SETLK, &read_lck); + delete infile; + infile = NULL; + } else { + // A LINE HAS BEEN SUCCESSFULLY READ, EXECUTE THE CONTENTS OF THE LOOP + executionLevel++; + } + } else { + write_lck.l_type = F_UNLCK; // REMOVE THE WRITE LOCK + fcntl(write_fd, F_SETLK, &write_lck); + delete outfile; + + read_lck.l_type = F_UNLCK; // REMOVE THE READ LOCK + fcntl(read_fd, F_SETLK, &read_lck); + delete infile; + + rename(out_name, in_name); + + outfile = NULL; + infile = NULL; + } + } + } + } + } else if (!strcmp(word[0], "while")) { + currentLevel++; + /* THIS LOOP COMES BACK TO THE START OF THE LINE CURRENTLY + EXECUTING, NOT THE LINE AFTER */ + top_of_while = before_command; + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else if (condition()) { + executionLevel++; + } + } else if (!strcmp(word[0], "whileall")) { + currentLevel++; + /* THIS LOOP COMES BACK TO THE START OF THE LINE CURRENTLY + EXECUTING, NOT THE LINE AFTER */ + top_of_while = before_command; + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else if (and_condition()) { + executionLevel++; + } + } else if (!strcmp(word[0], "loop")) { + /* THE LOOP COMMAND LOOPS ONCE FOR EACH DEFINED + * OBJECT (FOREACH) */ +#ifdef GLK + top_of_loop = g_vm->glk_stream_get_position(game_stream); +#else + top_of_loop = ftell(file); +#endif + if (word[1] == NULL) { + // IF NONE IS SUPPLIED DEFAULT TO noun3 + loop_integer = &noun[2]; + } else { + // STORE THE CONTAINER TO PUT THE CURRENT OBJECT IN + loop_integer = container_resolve(word[1]); + + // IF THE SUPPLIED CONTAINER CAN'T BE RESOLVED + // DEFAULT TO noun3 + if (loop_integer == NULL) + loop_integer = &noun[2]; + } + + // SET THE VALUE OF THE LOOP INDEX TO POINT TO THE FIRST OBJECT + *loop_integer = 1; + + } else if (!strcmp(word[0], "endloop")) { + if (top_of_loop == FALSE) { + sprintf(error_buffer, NO_LOOP, executing_function->name); + log_error(error_buffer, PLUS_STDOUT); + } else { + *loop_integer += 1; + if (*loop_integer > objects) { + top_of_loop = FALSE; + *loop_integer = 0; + } else { +#ifdef GLK + g_vm->glk_stream_set_position(game_stream, top_of_loop, seekmode_Start); +#else + fseek(file, top_of_loop, SEEK_SET); +#endif + } + } + } else if (!strcmp(word[0], "select")) { + /* THE SELECT COMMAND LOOPS ONCE FOR EACH DEFINED + * OBJECT THAT MATCHES THE SUPPLIED CRITERION */ +#ifdef GLK + top_of_select = g_vm->glk_stream_get_position(game_stream); +#else + top_of_select = ftell(file); +#endif + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else if (word[2] == NULL) { + // IF NONE IS SUPPLIED DEFAULT TO noun3 + select_integer = &noun[2]; + } else { + // STORE THE CONTAINER TO PUT THE CURRENT OBJECT IN + select_integer = container_resolve(word[2]); + + // IF THE SUPPLIED CONTAINER CAN'T BE RESOLVED + // DEFAULT TO noun3 + if (select_integer == NULL) { + select_integer = &noun[2]; + } + } + + // SET THE VALUE OF THE SELECT INDEX TO ONE BEFORE THE + // FIRST OBJECT. THE NEXT FUNCTION AUTOMATICALLY INCREMENTS + // THE INDEX BY ONE AT THE START OF THE WHILE LOOP. + *select_integer = 0; + + if (word[1][0] == '!') { + criterion_negate = TRUE; + strcpy(argument_buffer, &word[1][1]); + } else { + criterion_negate = FALSE; + strcpy(argument_buffer, word[1]); + } + + // DETERMINE THE CRITERION FOR SELETION + if (!strcmp(argument_buffer, "*held") + || !strcmp(argument_buffer, "*here") + || !strcmp(argument_buffer, "*anywhere") + || !strcmp(argument_buffer, "*present")) { + criterion_type = CRI_SCOPE; + strncpy(scope_criterion, argument_buffer, 20); + } else if ((criterion_value = attribute_resolve(argument_buffer))) { + criterion_type = CRI_ATTRIBUTE; + } else if ((criterion_value = user_attribute_resolve(argument_buffer))) { + criterion_type = CRI_USER_ATTRIBUTE; + } else { + // USE VALUE OF AS A CATCH ALL IF IT IS NOT AN ATTRIBUTE OR SCOPE + criterion_value = value_of(argument_buffer); + + if (value_resolved) { + criterion_type = CRI_PARENT; + } else { + // CAN'T RESOLVE CRITERION + criterion_type = CRI_NONE; + } + } + + if (criterion_type != CRI_NONE) { + if (select_next() == FALSE) { + *select_integer = 0; + top_of_select = 0; + } + } else { + *select_integer = 0; + } + + if (*select_integer == 0) { + // THERE ARE NO MATCHING OBJECTS SO JUMP TO THE endselect +#ifdef GLK + glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024); +#else + fgets(text_buffer, 1024, file); +#endif + + if (encrypted) jacl_decrypt(text_buffer); + + while (text_buffer[0] != '}') { + encapsulate(); + if (word[0] != NULL && !strcmp(word[0], "endselect")) { + break; + } +#ifdef GLK + glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024); +#else + fgets(text_buffer, 1024, file); +#endif + } + } + } else if (!strcmp(word[0], "endselect")) { + if (top_of_select == FALSE) { + sprintf(error_buffer, NO_LOOP, executing_function->name); + log_error(error_buffer, PLUS_STDOUT); + } else { + if (select_next(/* select_integer, criterion_type, criterion_value, scope_criterion */)) { +#ifdef GLK + g_vm->glk_stream_set_position(game_stream, top_of_select, seekmode_Start); +#else + fseek(file, top_of_select, SEEK_SET); +#endif + } else { + *select_integer = 0; + top_of_select = 0; + } + } + } else if (!strcmp(word[0], "break")) { + currentLevel++; + executionLevel--; +#ifdef GLK + } else if (!strcmp(word[0], "cursor")) { + if (word[2] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else { + if (current_window == statuswin) { + g_vm->glk_window_move_cursor(statuswin, value_of(word[1], TRUE), value_of(word[2], TRUE)); + } else { + log_error(BAD_CURSOR, PLUS_STDOUT); + } + } + } else if (!strcmp(word[0], "stop")) { + int channel; + + if (SOUND_SUPPORTED->value) { + /* SET THE CHANNEL TO STOP, IF SUPPLIED */ + if (word[1] == NULL) { + channel = 0; + } else { + channel = value_of(word[1], TRUE); + + /* SANITY CHECK THE CHANNEL SELECTED */ + if (channel < 0 || channel > 7) { + channel = 0; + } + } + g_vm->glk_schannel_stop(sound_channel[channel]); + } + } else if (!strcmp(word[0], "volume")) { + int channel, volume; + + if (SOUND_SUPPORTED->value) { + /* SET THE CHANNEL TO STOP, IF SUPPLIED */ + if (word[2] == NULL) { + channel = 0; + } else { + channel = value_of(word[2], TRUE); + + /* SANITY CHECK THE CHANNEL SELECTED */ + if (channel < 0 || channel > 7) { + channel = 0; + } + } + + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + volume = value_of(word[1], TRUE); + + /* SANITY CHECK THE CHANNEL SELECTED */ + if (volume < 0) { + volume = 0; + } + + if (volume > 100) { + volume = 100; + } + + /* STORE A COPY OF THE CURRENT VOLUME FOR ACCESS + * FROM JACL CODE */ + sprintf(temp_buffer, "volume[%d]", channel); + cinteger_resolve(temp_buffer)->value = volume; + + /* NOW SCALE THE 0-100 VOLUME TO THE 0-65536 EXPECTED + * BY Glk */ + volume = volume * 655; + + /* SET THE VOLUME */ + g_vm->glk_schannel_set_volume(sound_channel[channel], (glui32) volume); + } + } + } else if (!strcmp(word[0], "timer")) { + if (TIMER_SUPPORTED->value && TIMER_ENABLED->value) { + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + index = value_of(word[1], TRUE); + /* DON'T ALLOW NEGATIVE VALUES, BUT NO UPPER LIMIT */ + if (index < 0) index = 0; + + /* SET THE GLK TIMER */ + g_vm->glk_request_timer_events((glui32) index); + + /* EXPOSE THE CURRENT VALUE THROUGH A JACL CONSTANT + SO THAT GAME CODE CAN READ THE IT */ + cinteger_resolve("timer")->value = index; + } + } + } else if (!strcmp(word[0], "sound")) { + int channel; + glui32 repeats; + + if (SOUND_SUPPORTED->value && SOUND_ENABLED->value) { + /* SET THE CHANNEL TO USE, IF SUPPLIED */ + if (word[2] == NULL) { + channel = 0; + } else { + channel = value_of(word[2], TRUE); + + /* SANITY CHECK THE CHANNEL SELECTED */ + if (channel < 0 || channel > 7) { + channel = 0; + } + } + + /* SET THE NUMBER OF REPEATS, IF SUPPLIED */ + if (word[3] == NULL) { + repeats = 1; + } else { + repeats = value_of(word[3], TRUE); + } + + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + if (g_vm->glk_schannel_play_ext(sound_channel[channel], (glui32) value_of(word[1], TRUE), repeats, channel + 1) == 0) { + /* THE CHANNEL NUMBER IS PASSED SO THAT THE SOUND + * NOTIFICATION EVENT CAN USE THE INFORMATION + * IT HAS 1 ADDED TO IT SO THAT IT IS A NON-ZERO + * NUMBER AND THE EVENT IS ACTIVATED */ + sprintf(error_buffer, "Unable to play sound: %ld", value_of(word[1], FALSE)); + log_error(error_buffer, PLUS_STDERR); + } + } + } + } else if (!strcmp(word[0], "image")) { + if (GRAPHICS_SUPPORTED->value && GRAPHICS_ENABLED->value) { + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + if (g_vm->glk_image_draw(mainwin, (glui32) value_of(word[1], TRUE), imagealign_InlineDown, 0) == 0) { + sprintf(error_buffer, "Unable to draw image: %ld", value_of(word[1], FALSE)); + log_error(error_buffer, PLUS_STDERR); + } + } + } + } else if (!strcmp(word[0], "askstring") || !strcmp(word[0], "getstring")) { + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else { + /* GET A POINTER TO THE STRING BEING MODIFIED */ + if ((resolved_string = string_resolve(word[1])) == NULL) { + unkstrrun(word[1]); + return (exit_function(TRUE)); + } + + // PROMPT THE USER TO INPUT A STRING AND STORE IT IN THE + // RESOLVED VARIABLE + get_string(resolved_string->value); + } + + } else if (!strcmp(word[0], "asknumber") || !strcmp(word[0], "getnumber")) { + int low, high; + + int insist = FALSE; + + /* THE ONLY DIFFERENCE WITH THE getnumber COMMAND IS THAT + * IT INSISTS THE PLAYER GIVES A LEGAL RESPONSE */ + if (!strcmp(word[0], "getnumber")) { + insist = TRUE; + } + + if (word[3] != NULL) { + ask_integer = container_resolve(word[1]); + if (ask_integer == NULL) { + unkvarrun(word[1]); + return (exit_function(TRUE)); + } + + low = value_of(word[2], TRUE); + high = value_of(word[3], TRUE); + + if (high == -1 || low == -1) { + return (exit_function(TRUE)); + } + + *ask_integer = get_number(insist, low, high); + } else { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } + } else if (!strcmp(word[0], "getyesorno")) { + if (word[1] != NULL) { + ask_integer = container_resolve(word[1]); + if (ask_integer == NULL) { + unkvarrun(word[1]); + return (exit_function(TRUE)); + } + + *ask_integer = get_yes_or_no(); + } else { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } + } else if (!strcmp(word[0], "clear")) { + if (!walkthru_running) { + g_vm->glk_window_clear(current_window); + } + } else if (!strcmp(word[0], "terminate")) { + terminate(0); + } else if (!strcmp(word[0], "more")) { + if (word[1] == NULL) { + more("[MORE]"); + } else { + more(word[1]); + } + } else if (!strcmp(word[0], "style")) { + /* THIS COMMAND IS USED TO OUTPUT ANSI CODES OR SET GLK + * STREAM STYLES */ + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + if (!strcmp(word[1], "bold") + || !strcmp(word[1], "emphasised")) { + g_vm->glk_set_style(style_Emphasized); + } else if (!strcmp(word[1], "note")) { + g_vm->glk_set_style(style_Note); + } else if (!strcmp(word[1], "input")) { + g_vm->glk_set_style(style_Input); + } else if (!strcmp(word[1], "header")) { + g_vm->glk_set_style(style_Header); + } else if (!strcmp(word[1], "subheader")) { + g_vm->glk_set_style(style_Subheader); + } else if (!strcmp(word[1], "reverse") + || !strcmp(word[1], "inverse")) { + if (current_window == mainwin) { + g_vm->glk_set_style(style_User2); + } else { + g_vm->glk_set_style(style_User1); + } + } else if (!strcmp(word[1], "pre") + || !strcmp(word[1], "preformatted")) { + g_vm->glk_set_style(style_Preformatted); + } else if (!strcmp(word[1], "normal")) { + g_vm->glk_set_style(style_Normal); + } + } + } else if (!strcmp(word[0], "flush")) { + } else if (!strcmp(word[0], "hyperlink")) { + /* OUTPUT LINK TEXT AS PLAIN TEXT UNDER Glk */ + if (word[2] == NULL) { + noproprun(); + pop_stack(); + return (TRUE); + } else { + write_text(text_of_word(1)); + } +#else +#ifdef __NDS__ + } else if (!strcmp(word[0], "flush")) { + jflush(); + } else if (!strcmp(word[0], "cursor")) { + if (word[2] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else { + printf("\x1b[%d;%dH", (int) value_of(word[1], TRUE), (int) value_of(word[2], TRUE)); + } + } else if (!strcmp(word[0], "stop")) { + } else if (!strcmp(word[0], "volume")) { + } else if (!strcmp(word[0], "timer")) { + } else if (!strcmp(word[0], "sound")) { + } else if (!strcmp(word[0], "image")) { + } else if (!strcmp(word[0], "askstring") || !strcmp(word[0], "getstring")) { + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else { + /* GET A POINTER TO THE STRING BEING MODIFIED */ + if ((resolved_string = string_resolve(word[1])) == NULL) { + unkstrrun(word[1]); + return (exit_function(TRUE)); + } + + // PROMPT THE USER TO INPUT A STRING AND STORE IT IN THE + // RESOLVED VARIABLE + get_string(resolved_string->value); + } + + } else if (!strcmp(word[0], "asknumber") || !strcmp(word[0], "getnumber")) { + int low, high; + + int insist = FALSE; + + /* THE ONLY DIFFERENCE WITH THE getnumber COMMAND IS THAT + * IT INSISTS THE PLAYER GIVES A LEGAL RESPONSE */ + if (!strcmp(word[0], "getnumber")) { + insist = TRUE; + } + + if (word[3] != NULL) { + ask_integer = container_resolve(word[1]); + if (ask_integer == NULL) { + unkvarrun(word[1]); + return (exit_function(TRUE)); + } + + low = value_of(word[2], TRUE); + high = value_of(word[3], TRUE); + + if (high == -1 || low == -1) { + return (exit_function(TRUE)); + } + + *ask_integer = get_number(insist, low, high); + } else { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } + } else if (!strcmp(word[0], "getyesorno")) { + if (word[1] != NULL) { + ask_integer = container_resolve(word[1]); + if (ask_integer == NULL) { + unkvarrun(word[1]); + return (exit_function(TRUE)); + } + + *ask_integer = get_yes_or_no(); + } else { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } + } else if (!strcmp(word[0], "clear")) { + clrscrn(); + } else if (!strcmp(word[0], "terminate")) { + terminate(0); + } else if (!strcmp(word[0], "more")) { + if (word[1] == NULL) { + more("[MORE]"); + } else { + more(word[1]); + } + } else if (!strcmp(word[0], "style")) { + /* THIS COMMAND IS USED TO OUTPUT ANSI CODES OR SET GLK + * STREAM STYLES */ + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + if (!strcmp(word[1], "bold") + || !strcmp(word[1], "emphasised")) { + printf("\x1b[37;1m"); // SET TO BRIGHT WHITE + bold_mode = TRUE; + } else if (!strcmp(word[1], "note")) { + printf("\x1b[34;1m"); // SET TO BRIGHT BLUE + note_mode = TRUE; + } else if (!strcmp(word[1], "input")) { + printf("\x1b[32;0m"); // SET TO DIM GREEN + input_mode = TRUE; + } else if (!strcmp(word[1], "header")) { + printf("\x1b[37;0m"); // SET TO DIM WHITE + } else if (!strcmp(word[1], "subheader")) { + printf("\x1b[33;1m"); // SET TO BRIGHT YELLOW + subheader_mode = TRUE; + } else if (!strcmp(word[1], "reverse") + || !strcmp(word[1], "inverse")) { + printf("\x1b[7m"); // SET TO DIM WHITE + reverse_mode = TRUE; + } else if (!strcmp(word[1], "pre") + || !strcmp(word[1], "preformatted")) { + printf("\x1b[37;0m"); // SET TO DIM WHITE + pre_mode = TRUE; + } else if (!strcmp(word[1], "normal")) { + printf("\x1b[37;0m"); // SET TO DIM WHITE + bold_mode = FALSE; + pre_mode = FALSE; + reverse_mode = FALSE; + input_mode = FALSE; + subheader_mode = FALSE; + note_mode = FALSE; + } + } + } else if (!strcmp(word[0], "hyperlink")) { + /* OUTPUT LINK TEXT AS PLAIN TEXT UNDER Glk */ + if (word[2] == NULL) { + noproprun(); + pop_stack(); + return (TRUE); + } else { + write_text(text_of_word(1)); + } +#else + /* HERE STARTS THE CGIJACL-ONLY FUNCTIONS */ + } else if (!strcmp(word[0], "option")) { + /* USED TO ADD AN OPTION TO AN HTML LIST */ + if (word[1] == NULL) { + noproprun(); + pop_stack(); + return (TRUE); + } else { + index = value_of(word[1]); + if (word[2] != NULL) { + sprintf(option_buffer, "<option value=\"%d\">", + index); + } else { + object_names(index, temp_buffer); + sprintf(option_buffer, "<option value=\"%s\">", temp_buffer); + } + + write_text(option_buffer); + list_output(index, TRUE); + write_text(temp_buffer); + + } + } else if (!strcmp(word[0], "getenv")) { + struct string_type *resolved_setstring = NULL; + + if (word[2] == NULL) { + noproprun(); + pop_stack(); + return (TRUE); + } else { + // GET A POINTER TO THE STRING BEING MODIFIED + if ((resolved_setstring = string_resolve(word[1])) == NULL) { + unkstrrun(word[1]); + return (exit_function(TRUE)); + } + + // COPY THE VARIABLE OF THE CGI VARIABLE INTO THE SPECIFIED STRING VARIABLE + if (getenv(text_of_word(2)) != NULL) { + strncpy(resolved_setstring->value, getenv(text_of_word(2)), 255); + } else { + strncpy(resolved_setstring->value, "", 255); + } + } + } else if (!strcmp(word[0], "button")) { + /* USED TO CREATE AN HTML BUTTON */ + if (word[1] == NULL) { + noproprun(); + pop_stack(); + return (TRUE); + } + if (word[2] != NULL) { + sprintf(option_buffer, "<input class=~button~ type=~image~ src=~%s~ name=~verb~ value=~", text_of_word(2)); + strcat(option_buffer, text_of_word(1)); + strcat(option_buffer, "~>"); + write_text(option_buffer); + } else { + sprintf(option_buffer, "<input class=~button~ type=~submit~ style=~width: 90px; margin: 5px;~ name=~verb~ value=~%s~>", text_of_word(1)); + write_text(option_buffer); + } + } else if (!strcmp(word[0], "hidden")) { + sprintf(temp_buffer, "<INPUT TYPE=\"hidden\" NAME=\"user_id\" VALUE=\"%s\">", user_id); + write_text(temp_buffer); + } else if (!strcmp(word[0], "control")) { + /* USED TO CREATE A HYPERLINK THAT IS AN IMAGE */ + if (word[2] == NULL) { + noproprun(); + pop_stack(); + return (TRUE); + } else { + sprintf(option_buffer, "<a href=\"?command=%s&user_id=%s\"><img border=0 SRC=\"", text_of_word(2), user_id); + strcat(option_buffer, text_of_word(1)); + strcat(option_buffer, "\"></a>"); + write_text(option_buffer); + } + } else if (!strcmp(word[0], "hyperlink") || !strcmp(word[0], "hyperlinkNE")) { + string_buffer[0] = 0; + + /* USED TO CREATE A HYPERLINK WITH SESSION INFORMATION INCLUDED */ + if (word[2] == NULL) { + noproprun(); + pop_stack(); + return (TRUE); + } else { + char *encoded; + + if (!strcmp(word[0], "hyperlink")) { + encoded = url_encode(text_of_word(2)); + } else { + encoded = text_of_word(2); + } + + if (word[3] == NULL) { + sprintf(string_buffer, "<a href=\"?command=%s&user_id=%s\">", encoded, user_id); + strcat(string_buffer, text_of_word(1)); + strcat(string_buffer, "</a>"); + } else { + sprintf(string_buffer, "<a class=\"%s\" href=\"?command=", text_of_word(3)); + strcat(string_buffer, encoded); + sprintf(option_buffer, "&user_id=%s\">%s</a>", user_id, text_of_word(1)); + strcat(string_buffer, option_buffer); + } + + if (!strcmp(word[0], "hyperlink")) { + free(encoded); + } + + write_text(string_buffer); + } + } else if (!strcmp(word[0], "prompt")) { + /* USED TO OUTPUT A HTML INPUT CONTROL THAT CONTAINS SESSION INFORMATION */ + if (word[1] != NULL) { + sprintf(temp_buffer, "<input id=\"JACLCommandPrompt\" type=text name=~command~ onKeyPress=~%s~>\n", word[1]); + write_text(temp_buffer); + } else { + sprintf(temp_buffer, "<input id=\"JACLCommandPrompt\" type=text name=~command~>\n"); + write_text(temp_buffer); + } + sprintf(temp_buffer, "<input type=hidden name=\"user_id\" value=\"%s\">", user_id); + write_text(temp_buffer); + } else if (!strcmp(word[0], "style")) { + /* THIS COMMAND IS USED TO OUTPUT ANSI CODES OR SET GLK + * STREAM STYLES */ + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + if (!strcmp(word[1], "bold") + || !strcmp(word[1], "emphasised")) { + write_text("<b>"); + style_stack[style_index++] = BOLD; + } else if (!strcmp(word[1], "note")) { + write_text("<i>"); + style_stack[style_index++] = NOTE; + } else if (!strcmp(word[1], "input")) { + write_text("<i>"); + style_stack[style_index++] = INPUT; + } else if (!strcmp(word[1], "header")) { + write_text("<h1>"); + style_stack[style_index++] = HEADER; + } else if (!strcmp(word[1], "subheader")) { + write_text("<h2>"); + style_stack[style_index++] = SUBHEADER; + } else if (!strcmp(word[1], "reverse") + || !strcmp(word[1], "inverse")) { + write_text("<b>"); + style_stack[style_index++] = REVERSE; + } else if (!strcmp(word[1], "pre") + || !strcmp(word[1], "preformatted")) { + write_text("<pre>"); + style_stack[style_index++] = PRE; + } else if (!strcmp(word[1], "normal")) { + style_index--; + for (; style_index > -1; style_index--) { + switch (style_stack[style_index]) { + case BOLD: + write_text("</b>"); + break; + case NOTE: + write_text("</i>"); + break; + case INPUT: + write_text("</i>"); + break; + case HEADER: + write_text("</h1>"); + break; + case SUBHEADER: + write_text("</h2>"); + break; + case REVERSE: + write_text("</b>"); + break; + case PRE: + write_text("</pre>"); + break; + } + } + style_index = 0; + } + } + /* THESE FINAL COMMANDS HAVE NO EFFECT UNDER CGIJACL + AND THERE IS NO HARM IN IGNORING THEM */ + } else if (!strcmp(word[0], "flush")) { + } else if (!strcmp(word[0], "image")) { + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else { + if (word[2] == NULL) { + sprintf(option_buffer, "<img src=~%s~>", text_of_word(1)); + } else { + sprintf(option_buffer, "<img class=~%s~ src=~%s~>", text_of_word(2), text_of_word(1)); + } + + write_text(option_buffer); + } + } else if (!strcmp(word[0], "sound")) { + if (word[2] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else { + write_text("<audio autoplay=~autoplay~>"); + if (word[3] == NULL) { + sprintf(option_buffer, "<source src=~%s~ type=~%s~>", text_of_word(1), text_of_word(2)); + write_text(option_buffer); + } + write_text("</audio>"); + } + } else if (!strcmp(word[0], "cursor")) { + } else if (!strcmp(word[0], "timer")) { + } else if (!strcmp(word[0], "volume")) { + } else if (!strcmp(word[0], "askstring") || !strcmp(word[0], "getstring")) { + } else if (!strcmp(word[0], "asknumber") || !strcmp(word[0], "getnumber")) { + } else if (!strcmp(word[0], "getyesorno")) { + } else if (!strcmp(word[0], "clear")) { + } else if (!strcmp(word[0], "more")) { + } else if (!strcmp(word[0], "terminate")) { +#endif +#endif + } else if (!strcmp(word[0], "proxy")) { + /* THE PROXY COMMAND ISSUES A MOVE ON THE PLAYER'S BEHALF + * ALL STATE MUST BE SAVED SO THE CURRENT MOVE CAN CONTINUE + * ONCE THE PROXIED MOVE IS COMPLETE */ +#ifdef GLK + push_stack(g_vm->glk_stream_get_position(game_stream)); +#else + push_stack(ftell(file)); +#endif + push_proxy(); + + build_proxy(); + + // TEXT BUFFER IS THE NORMAL ARRAY FOR HOLDING THE PLAYERS + // MOVE FOR PROCESSING + strncpy(text_buffer, proxy_buffer, 1024); + + command_encapsulate(); + + jacl_truncate(); + + preparse(); + + pop_proxy(); + + pop_stack(); + } else if (!strcmp(word[0], "override")) { + /* TELLS THE INTERPRETER TO LOOK FOR AN _override FUNCTION + * TO EXECUTE IN PLACE OF ANY CODE THAT FOLLOWS THIS LINE. + * THIS COMMAND IS USED EXCLUSIVELY IN GLOBAL FUNCTIONS + * ASSOCIATED WITH GRAMMAR LINES */ + if (execute(override) == TRUE) { + return (exit_function(TRUE)); + } else { + if (execute(default_function) == TRUE) { + return (exit_function(TRUE)); + } + } + } else if (!strcmp(word[0], "execute") || !strcmp(word[0], "call")) { + /* CALLS ANOTHER JACL FUNCTION */ + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + /* RESOLVE ALL THE TEXT AND STORE IT IN A TEMPORARY BUFFER*/ + string_buffer[0] = 0; + + for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) { + strcat(string_buffer, arg_text_of_word(counter)); + } + + if (function_resolve(string_buffer) == NULL && !strcmp(word[0], "execute")) { + char *argstart; + + /* REMOVE ANY PARAMETERS FROM FUNCTION NAME + BEFORE DISPLAYING ERROR MESSAGE */ + argstart = strchr(string_buffer, '<'); + if (argstart != NULL) + *argstart = 0; + + sprintf(error_buffer, UNDEFINED_FUNCTION, executing_function->name, string_buffer); + log_error(error_buffer, PLUS_STDOUT); + } else { + execute(string_buffer); + } + } + } else if (!strcmp(word[0], "points")) { + /* INCREASE THE PLAYER'S SCORE AND POTENTIALLY INFORM THEM OF THE INCREASE */ + if (word[1] != NULL) { + SCORE->value += value_of(word[1], TRUE); + if (NOTIFY->value) { +#ifdef GLK + g_vm->glk_set_style(style_Note); +#else +#ifdef __NDS__ + printf("\x1b[34;1m"); // SET TO BRIGHT BLUE + note_mode = TRUE; +#else + write_text("<b><i>"); +#endif +#endif + write_text(cstring_resolve("SCORE_UP")->value); + sprintf(temp_buffer, "%ld", value_of(word[1], TRUE)); + write_text(temp_buffer); + if (value_of(word[1], TRUE) == 1) { + write_text(cstring_resolve("POINT")->value); + } else { + write_text(cstring_resolve("POINTS")->value); + } +#ifdef GLK + g_vm->glk_set_style(style_Normal); +#else +#ifdef __NDS__ + printf("\x1b[37;0m"); // SET TO DIM WHITE + note_mode = FALSE; +#else + write_text("</i></b>"); +#endif +#endif + } + } else { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } + } else if (!strcmp(word[0], "print")) { + int non_space = FALSE; + + // DISPLAYS A BLOCK OF PLAIN TEXT UNTIL IT FINDS A + // LINE THAT STARTS WITH A '.' OR A '}' +#ifdef GLK + glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024); +#else + fgets(text_buffer, 1024, file); +#endif + + if (encrypted) jacl_decrypt(text_buffer); + + while (text_buffer[0] != '.' && text_buffer[0] != '}') { + index = 0; + non_space = FALSE; + + /* REMOVE ANY NEWLINE CHARACTERS */ + while (text_buffer[index] != 0) { + if (text_buffer[index] == '|' && non_space == FALSE) { + /* THE BAR CHARACTER IS CHANGED TO A SPACE TO + * ALLOW INDENTING OF NEW PARAGRAPHS ETC */ + text_buffer[index] = ' '; + } else if (text_buffer[index] == '\r') { + text_buffer[index] = 0; + break; + } else if (text_buffer[index] == '\n') { + text_buffer[index] = 0; + break; + } else if (text_buffer[index] != ' ' && text_buffer[index] != '\t') { + non_space = TRUE; + } + + index++; + } + + if (text_buffer[0] != 0) { + // CHECK IF THERE IS THE NEED TO ADD AN + // IMPLICIT SPACE + index = strlen(text_buffer); + + if (text_buffer[index - 1] == '\\') { + // A BACKSLASH IS USED TO INDICATE AN IMPLICIT + // SPACE SHOULD NOT BE PRINTED + text_buffer[index - 1] = 0; + } else if (text_buffer[index - 1] != '^') { + // ADD AN IMPLICIT SPACE IF THE PREVIOUS LINE + // DIDN'T END WITH A CARRIAGE RETURN + strcat(text_buffer, " "); + } + + // OUTPUT THE LINE READ AS PLAIN TEXT + write_text(text_buffer); + } + + // GET THE NEXT LINE +#ifdef GLK + glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024); +#else + fgets(text_buffer, 1024, file); +#endif + + if (encrypted) jacl_decrypt(text_buffer); + } + } else if (!strcmp(word[0], "mesg")) { + for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) { + warning("%s", text_of_word(counter)); + } + } else if (!strcmp(word[0], "error")) { + write_text("ERROR: In function ~"); + write_text(executing_function->name); + write_text("~, "); + for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) { + write_text(text_of_word(counter)); + } + } else if (!strcmp(word[0], "debug") && DEBUG->value) { + write_text("DEBUG: "); + for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) { + write_text(text_of_word(counter)); + } + } else if (!strcmp(word[0], "write")) { + for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) { + output = text_of_word(counter); + if (*output != 0) { + // IF THE OUTPUT ISN'T AN EMPTY STRING, DISPLAY IT + write_text(output); + } + } + } else if (!strcmp(word[0], "length")) { + if (word[2] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else { + if ((container = container_resolve(word[1])) == NULL) { + unkvarrun(word[1]); + return (exit_function(TRUE)); + } + + *container = strlen(text_of(word[2])); + } + } else if (!strcmp(word[0], "savegame")) { + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + if ((container = container_resolve(word[1])) == NULL) { + unkvarrun(word[1]); + return (exit_function(TRUE)); + } else { + if (word[2] == NULL) { + *container = save_interaction(NULL); + } else { + *container = save_interaction(arg_text_of_word(2)); + } + } + } + } else if (!strcmp(word[0], "restoregame")) { + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + if ((container = container_resolve(word[1])) == NULL) { + unkvarrun(word[1]); + return (exit_function(TRUE)); + } else { + if (word[2] == NULL) { + *container = restore_interaction(NULL); + } else { + *container = restore_interaction(arg_text_of_word(2)); + } + } + } + } else if (!strcmp(word[0], "restartgame")) { + restart_game(); + execute("+intro"); + eachturn(); +#ifdef GLK + } else if (!strcmp(word[0], "undomove")) { + undoing(); + } else if (!strcmp(word[0], "updatestatus")) { + status_line(); +#else + } else if (!strcmp(word[0], "undomove")) { + } else if (!strcmp(word[0], "updatestatus")) { +#endif + } else if (!strcmp(word[0], "split")) { + + // 0 1 2 3 4 + // split counter source delimiter destination + + int *split_container; + char split_buffer[256] = ""; + char container_buffer[256] = ""; + char delimiter[256] = ""; + char *match = NULL; + struct string_type *resolved_splitstring = NULL; + + strcpy(split_buffer, text_of_word(2)); + strcpy(delimiter, text_of_word(3)); + + char *source = split_buffer; + + if (word[4] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else { + split_container = container_resolve(var_text_of_word(1)); + + if (split_container == NULL) { + unkvarrun(var_text_of_word(1)); + return (exit_function(TRUE)); + } else { + *split_container = 0; + match = source; // THERE IS ALWAYS ONE MATCH, EVEN IF + // NO DELIMETERS ARE FOUND + + while ((match = strstr(source, delimiter))) { + *match = 0; + strcpy(container_buffer, var_text_of_word(4)); + strcat(container_buffer, "["); + sprintf(integer_buffer, "%d", *split_container); + strcat(container_buffer, integer_buffer); + strcat(container_buffer, "]"); + + if ((resolved_splitstring = string_resolve(container_buffer)) == NULL) { + unkstrrun(var_text_of_word(4)); + return (exit_function(TRUE)); + } else { + strcpy(resolved_splitstring->value, source); + source = match + strlen(delimiter); + (*split_container)++; + } + } + strcpy(container_buffer, var_text_of_word(4)); + strcat(container_buffer, "["); + sprintf(integer_buffer, "%d", *split_container); + strcat(container_buffer, integer_buffer); + strcat(container_buffer, "]"); + + if ((resolved_splitstring = string_resolve(container_buffer)) == NULL) { + unkstrrun(word[1]); + return (exit_function(TRUE)); + } else { + strcpy(resolved_splitstring->value, source); + (*split_container)++; + } + } + } + } else if (!strcmp(word[0], "setstring") || + !strcmp(word[0], "addstring")) { + char setstring_buffer[2048] = ""; + struct string_type *resolved_setstring = NULL; + + if (word[2] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else { + /* GET A POINTER TO THE STRING BEING MODIFIED */ + if ((resolved_setstring = string_resolve(var_text_of_word(1))) == NULL) { + unkstrrun(word[1]); + return (exit_function(TRUE)); + } + + /* RESOLVE ALL THE TEXT AND STORE IT IN A TEMPORARY BUFFER*/ + for (counter = 2; word[counter] != NULL && counter < MAX_WORDS; counter++) { + strcat(setstring_buffer, text_of_word(counter)); + } + + /* setstring_buffer IS NOW FILLED, COPY THE UP TO 256 BYTES OF + * IT INTO THE STRING */ + if (!strcmp(word[0], "setstring")) { + strncpy(resolved_setstring->value, setstring_buffer, 255); + } else { + /* CALCULATE HOW MUCH SPACE IS LEFT IN THE STRING */ + counter = 255 - strlen(resolved_setstring->value); + /* THIS IS A addstring COMMAND, SO USE STRNCAT INSTEAD */ + strncat(resolved_setstring->value, setstring_buffer, counter); + } + } + } else if (!strcmp(word[0], "padstring")) { + char setstring_buffer[2048] = ""; + struct string_type *resolved_setstring = NULL; + string_buffer[0] = 0; + + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else { + /* GET A POINTER TO THE STRING BEING MODIFIED */ + if ((resolved_setstring = string_resolve(word[1])) == NULL) { + unkstrrun(word[1]); + return (exit_function(TRUE)); + } + + index = value_of(word[3], TRUE); + + for (counter = 0; counter < index; counter++) { + strcat(setstring_buffer, text_of_word(2)); + } + + /* setstring_buffer IS NOW FILLED, COPY THE UP TO 256 BYTES OF + * IT INTO THE STRING */ + strncpy(resolved_setstring->value, setstring_buffer, 255); + } + } else if (!strcmp(word[0], "return")) { + /* RETURN FROM THIS FUNCTION, POSSIBLY RETURNING AN INTEGER VALUE */ + if (word[1] == NULL) { + return (exit_function(TRUE)); + } else { + index = value_of(word[1], TRUE); + return (exit_function(index)); + } + } else if (!strcmp(word[0], "position")) { + /* MOVE AN OBJECT TO ITS NEW X,Y COORDINATES BASED ON ITS CURRENT VALUES + * FOR x, y, bearing, velocity */ + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + object_1 = value_of(word[1], TRUE); + + if (object_1 < 1 || object_1 > objects) { + badptrrun(word[1], object_1); + return (exit_function(TRUE)); + } else { + new_position((double) object[object_1]->X, + (double) object[object_1]->Y, + (double) object[object_1]->BEARING, + (double) object[object_1]->VELOCITY); + + object[object_1]->X = new_x; + object[object_1]->Y = new_y; + } + } + } else if (!strcmp(word[0], "bearing")) { + /* CALCULATE THE BEARING BETWEEN TWO OBJECTS */ + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + if ((container = container_resolve(word[1])) == NULL) { + unkvarrun(word[1]); + return (exit_function(TRUE)); + } + + object_1 = value_of(word[2], TRUE); + + if (object_1 < 1 || object_1 > objects) { + badptrrun(word[2], object_1); + return (exit_function(TRUE)); + } else { + object_2 = value_of(word[3], TRUE); + + if (object_2 < 1 || object_2 > objects) { + badptrrun(word[3], object_2); + return (exit_function(TRUE)); + } else { + if (container != NULL + && object_1 != FALSE + && object_2 != FALSE) { + *container = bearing((double) object[object_1]->X, + (double) object[object_1]->Y, + (double) object[object_2]->X, + (double) object[object_2]->Y); + } + } + } + } + } else if (!strcmp(word[0], "distance")) { + /* CALCULATE THE DISTANCE BETWEEN TWO OBJECTS */ + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + container = container_resolve(word[1]); + + object_1 = value_of(word[2], TRUE); + + if (object_1 < 1 || object_1 > objects) { + badptrrun(word[2], object_1); + return (exit_function(TRUE)); + } else { + object_2 = value_of(word[3], TRUE); + + if (object_2 < 1 || object_2 > objects) { + badptrrun(word[3], object_2); + return (exit_function(TRUE)); + } else { + if (container != NULL + && object_1 != FALSE + && object_2 != FALSE) { + *container = distance((double) + object[object_1]->X, + (double) + object[object_1]->Y, + (double) + object[object_2]->X, + (double) + object[object_2]->Y); + } + } + } + } + } else if (!strcmp(word[0], "dir_to") || + !strcmp(word[0], "npc_to")) { + /* CALCULATE THE FIRST DIRECTION TO TRAVEL IN GET TO + * A SPECIFIED LOCATION */ + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + container = container_resolve(word[1]); + + object_1 = value_of(word[2], TRUE); + + if (object_1 < 1 || object_1 > objects) { + badptrrun(word[2], object_1); + return (exit_function(TRUE)); + } else { + object_2 = value_of(word[3], TRUE); + + if (object_2 < 1 || object_2 > objects) { + badptrrun(word[3], object_2); + return (exit_function(TRUE)); + } else { + if (container != NULL + && object_1 != FALSE + && object_2 != FALSE) { + if (!strcmp(word[0], "dir_to")) { + *container = find_route(object_1, object_2, TRUE); + } else { + *container = find_route(object_1, object_2, FALSE); + } + } + } + } + } + } else if (!strcmp(word[0], "set")) { + /* SET THE VALUE OF AN ELEMENT TO A SUPPLIED INTEGER */ + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + container = container_resolve(var_text_of_word(1)); + + if (container == NULL) { + unkvarrun(word[1]); + return (exit_function(TRUE)); + } else { + int mark = 2; // SET mark TO POINT TO THE FIRST OPERATOR + while (word[mark + 1] != NULL) { + counter = value_of(word[mark + 1], TRUE); + + if (word[mark][0] == '+') + *container += counter; + else if (word[mark][0] == '-') + *container -= counter; + else if (word[mark][0] == '*') + *container = *container * counter; + else if (word[mark][0] == '%') + *container = *container % counter; + else if (word[mark][0] == '/') { + if (counter == 0) { + sprintf(error_buffer, DIVIDE_BY_ZERO, + executing_function->name); + log_error(error_buffer, PLUS_STDOUT); + } else + *container = *container / counter; + } else if (!strcmp(word[mark], "locationof")) { + *container = grand_of(counter, FALSE); + } else if (!strcmp(word[mark], "grandof")) { + *container = grand_of(counter, TRUE); + } else if (word[mark][0] == '=') { + *container = counter; + } else { + sprintf(error_buffer, ILLEGAL_OPERATOR, + executing_function->name, + word[2]); + log_error(error_buffer, PLUS_STDOUT); + } + + mark += 2; + } + } + } + } else if (!strcmp(word[0], "ensure")) { + /* USED TO GIVE OR TAKE AN ATTRIBUTE TO OR FROM AND OBJECT */ + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } else { + if ((bit_mask = attribute_resolve(arg_text_of(word[3])))) { + index = value_of(word[1], TRUE); + if (index < 1 || index > objects) { + badptrrun(word[1], index); + return (exit_function(TRUE)); + } else { + if (!strcmp(word[2], "has")) { + object[index]->attributes = + object[index]->attributes | bit_mask; + } else if (!strcmp(word[2], "hasnt")) { + bit_mask = ~bit_mask; + object[index]->attributes = + object[index]->attributes & bit_mask; + } + } + } else if ((bit_mask = user_attribute_resolve(arg_text_of(word[3])))) { + index = value_of(word[1], TRUE); + if (index < 1 || index > objects) { + badptrrun(word[1], index); + return (exit_function(TRUE)); + } else { + if (!strcmp(word[2], "has")) { + object[index]->user_attributes = + object[index]->user_attributes | bit_mask; + } else if (!strcmp(word[2], "hasnt")) { + bit_mask = ~bit_mask; + object[index]->user_attributes = + object[index]->user_attributes & bit_mask; + } + } + } else { + unkattrun(3); + return (exit_function(TRUE)); + } + } + } else if (!strcmp(word[0], "append")) { + int first = TRUE; + + if (word[2] == NULL) { + // NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND + noproprun(); + return (exit_function(TRUE)); + } else { + strcpy(temp_buffer, data_directory); + strcat(temp_buffer, prefix); + strcat(temp_buffer, "-"); + strcat(temp_buffer, text_of_word(1)); + strcat(temp_buffer, ".csv"); + + outfile = File::openForWriting(temp_buffer); + + if (outfile == NULL) { + sprintf(error_buffer, "Failed to open file %s: %s\n", temp_buffer, strerror(errno)); + log_error(error_buffer, PLUS_STDOUT); + } else { + for (counter = 2; word[counter] != NULL && counter < MAX_WORDS; counter++) { + output = text_of_word(counter); + if (*output != 0) { + if (first == FALSE) { + outfile->writeByte(','); + } + csv_fwrite(outfile, output, (size_t) strlen(output)); + first = FALSE; + } + } + + // TERMINATE THE LINE + outfile->writeByte('\n'); + + // FLUSH AND CLOSE THE FILE + outfile->flush(); + } + + delete outfile; + outfile = NULL; + } + } else if (!strcmp(word[0], "insert")) { + int first = TRUE; + + if (word[1] == NULL) { + // NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND + noproprun(); + return (exit_function(TRUE)); + } else { + if (outfile == NULL) { + log_error("Insert statement not inside an 'update' loop.", PLUS_STDOUT); + } else { + for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) { + output = text_of_word(counter); + if (*output != 0) { + if (first == FALSE) { + outfile->writeByte(','); + } + csv_fwrite(outfile, output, (size_t) strlen(output)); + first = FALSE; + } + } + + // TERMINATE THE LINE + outfile->writeByte('\n'); + } + } + } else if (!strcmp(word[0], "inspect")) { + if (word[1] == NULL) { + // NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND + noproprun(); + return (exit_function(TRUE)); + } else { + inspect(value_of(word[1], TRUE)); + } + } else if (!strcmp(word[0], "move")) { + /* THIS COMMAND IS USED TO MOVE AN OBJECT TO HAVE ANOTHER PARENT + * INCLUDING MODIFYING ALL QUANTITY VALUES BASED ON THE OBJECTS MASS */ + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(); + return (exit_function(TRUE)); + } + + index = value_of(word[1], TRUE); + if (index < 1 || index > objects) { + badptrrun(word[1], index); + return (exit_function(TRUE)); + } else { + object_2 = object[index]->PARENT; + if (object_2 && !(object[object_2]->attributes & LOCATION)) { + object[object_2]->QUANTITY += object[index]->MASS; + } + object_1 = value_of(word[3], TRUE); + if (object_1 < 1 || object_1 > objects) { + badptrrun(word[1], object_1); + return (exit_function(TRUE)); + } else { + object[index]->PARENT = object_1; + if (!(object[object_1]->attributes & LOCATION)) + object[object_1]->QUANTITY -= object[index]->MASS; + } + } + } else if (!strcmp(word[0], "ifstringall")) { + /* CHECK IF A STRING EQUALS OR CONTAINS ANOTHER STRING */ + currentLevel++; + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else if (and_strcondition()) { + executionLevel++; + } + } else if (!strcmp(word[0], "ifstring")) { + /* CHECK IF A STRING EQUALS OR CONTAINS ANOTHER STRING */ + currentLevel++; + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else if (strcondition()) { + executionLevel++; + } + } else if (!strcmp(word[0], "ifexecute")) { + currentLevel++; + if (word[1] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else { + /* RESOLVE ALL THE TEXT AND STORE IT IN A TEMPORARY BUFFER*/ + string_buffer[0] = 0; + + for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) { + strcat(string_buffer, arg_text_of_word(counter)); + } + + if (execute(string_buffer)) { + executionLevel++; + } + } + } else if (!strcmp(word[0], "if")) { + currentLevel++; + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else if (condition()) { + executionLevel++; + } + } else if (!strcmp(word[0], "ifall")) { + currentLevel++; + if (word[3] == NULL) { + /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */ + noproprun(0); + return (exit_function(TRUE)); + } else if (and_condition()) { + executionLevel++; + } + } else { + sprintf(error_buffer, UNKNOWN_COMMAND, + executing_function->name, word[0]); + log_error(error_buffer, PLUS_STDOUT); + } + } else if (!strcmp(word[wp], "if") + || !strcmp(word[wp], "ifall") + || !strcmp(word[wp], "ifstring") + || !strcmp(word[wp], "ifstringall") + || !strcmp(word[wp], "ifexecute") + || !strcmp(word[wp], "iterate") + || !strcmp(word[wp], "update") + || !strcmp(word[wp], "while") + || !strcmp(word[wp], "whileall")) { + currentLevel++; + } + +#ifdef GLK + before_command = g_vm->glk_stream_get_position(game_stream); + glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024); +#else + before_command = ftell(file); + fgets(text_buffer, 1024, file); +#endif + if (encrypted) jacl_decrypt(text_buffer); + }; + + return (exit_function(TRUE)); +} + +int exit_function(int return_code) { + if (infile != NULL) { + read_lck.l_type = F_UNLCK; // SETTING A READ LOCK + fcntl(read_fd, F_SETLK, &read_lck); + delete infile; + infile = NULL; + } + + if (outfile != NULL) { + write_lck.l_type = F_UNLCK; // SETTING A WRITE LOCK + fcntl(write_fd, F_SETLK, &write_lck); + delete outfile; + outfile = NULL; + } + + /* POP THE STACK REGARDLESS OF THE RETURN CODE */ + pop_stack(); + + return (return_code); +} + +char *object_names(int object_index, char *names_buffer) { + /* THIS FUNCTION CREATES A LIST OF ALL AN OBJECT'S NAMES. + THE escape ARGUMENT INDICATES WHETHER A + SIGN SHOULD BE + USED IN PLACE OF A SPACE BETWEEN EACH OF THE NAMES */ + struct name_type *current_name = object[object_index]->first_name; + names_buffer[0] = 0; + + while (current_name != NULL) { + strcat(names_buffer, " "); + strcat(names_buffer, current_name->name); + current_name = current_name->next_name; + } + + return names_buffer; +} + +int distance(double x1, double y1, double x2, double y2) { + /* THIS FUNCTION CALCULATES THE DISTANCE BETWEEN TWO POINTS IN A + TWO-DIMENSIONAL PLANE */ + double delta_x, + delta_y; + double distance, + total; + + /* + * Object two in which quadrant compared to object one? 0 x = opp, y = + * ajd + 0 degrees 1 x = adj, y = opp + 90 degrees 2 x = opp, y = ajd + * + 180 degrees 3 x = adj, y = opp + 270 degrees + */ + + /* + * DETERMINE WHICH QUADRANT OBJECT TWO IS IN + */ + + if (x2 > x1) { + /* + * OBJECT TWO IS IN 1 OR 2 + */ + delta_x = x2 - x1; + if (y2 > y1) { + delta_y = y2 - y1; + } else { + delta_y = y1 - y2; + } + } else { + /* + * OBJECT TWO IS IN 3 OR 4 + */ + delta_x = x1 - x2; + if (y2 > y1) { + delta_y = y2 - y1; + } else { + delta_y = y1 - y2; + } + } + + delta_y = delta_y * delta_y; + delta_x = delta_x * delta_x; + + total = delta_y + delta_x; + + distance = sqrt(total); + + return ((int) distance); +} + +void new_position(double x1, double y1, double bearing, double velocity) { + double delta_x, + delta_y; + double radians; + + /* + * Object two in which quadrant compared to object one? 0 x = opp, y = + * ajd + 0 degrees 1 x = adj, y = opp + 90 degrees 2 x = opp, y = ajd + * + 180 degrees 3 x = adj, y = opp + 270 degrees + */ + + /* + * sin finds opp, cos finds adj + */ + + if (bearing < 91) { + radians = bearing * 2.0 * M_PI / 360.; + delta_x = velocity * sin(radians); + delta_y = velocity * cos(radians); + new_x = x1 + delta_x; + new_y = y1 + delta_y; + } else if (bearing < 181) { + bearing -= 90; + radians = bearing * 2.0 * M_PI / 360.; + delta_y = velocity * sin(radians); + delta_x = velocity * cos(radians); + new_x = x1 + delta_x; + new_y = y1 - delta_y; + } else if (bearing < 271) { + bearing -= 180; + radians = bearing * 2.0 * M_PI / 360.; + delta_x = velocity * sin(radians); + delta_y = velocity * cos(radians); + new_x = x1 - delta_x; + new_y = y1 - delta_y; + } else { + bearing -= 270; + radians = bearing * 2.0 * M_PI / 360.; + delta_y = velocity * sin(radians); + delta_x = velocity * cos(radians); + new_x = x1 - delta_x; + new_y = y1 + delta_y; + } +} + +int bearing(double x1, double y1, double x2, double y2) { + int quadrant; + double delta_x, + delta_y; + double oppoadj; + double bearing; + + /* + * Object two in which quadrant compared to object one? 0 x = opp, y = + * ajd + 0 degrees 1 x = adj, y = opp + 90 degrees 2 x = opp, y = ajd + * + 180 degrees 3 x = adj, y = opp + 270 degrees + */ + + if (x2 > x1) { + delta_x = x2 - x1; + if (y2 > y1) { + quadrant = 0; + delta_y = y2 - y1; + oppoadj = delta_x / delta_y; + } else { + quadrant = 1; + delta_y = y1 - y2; + oppoadj = delta_y / delta_x; + } + } else { + delta_x = x1 - x2; + if (y2 > y1) { + quadrant = 3; + delta_y = y2 - y1; + oppoadj = delta_y / delta_x; + } else { + quadrant = 2; + delta_y = y1 - y2; + oppoadj = delta_x / delta_y; + } + } + + bearing = atan(oppoadj); + bearing = bearing / (2.0 * M_PI) * 360.; + bearing = bearing + (90 * quadrant); + + return ((int) bearing); +} + +void set_arguments(char *function_call) { + /* THIS FUNCTION CREATES AN ARRAY OF JACL INTEGER CONSTANTS TO + REPRESENT THE ARGUMENTS PASSED TO A JACL FUNCTION */ + int index, + counter, + length; + int position = 0; /* STORE THE INDEX OF THE WORD */ + /* SETTING new_word TO FALSE SKIPS THE FIRST */ + /* WORD WHICH IS THE FUNCTION NAME */ + int new_word = FALSE; + + char *arg_ptr[MAX_WORDS]; + int arg_value[MAX_WORDS]; + + struct integer_type *resolved_integer; + struct cinteger_type *resolved_cinteger; + + /* SPLIT UP THE FUNCTION CALL STRING AND EXTRACT THE ARGUMENTS */ + length = strlen(function_call); + + for (index = 0; index < length; index++) { + if (function_call[index] == '<') { + argument_buffer[index] = 0; + new_word = TRUE; + } else { + // COPY THE CHARACTER FROM THE CALLED NAME INTO THE CURRENT + // ARGUMENT BUFFER + argument_buffer[index] = function_call[index]; + if (new_word) { + // THIS IS THE FIRST CHARACTER OF A NEW ARGUMENT SO STORE + // THE ADDRESS OF THIS CHARACTER IN THE ARGUMENT BUFFER + arg_ptr[position] = &argument_buffer[index]; + new_word = FALSE; + if (position < MAX_WORDS) + position++; + } + } + } + + argument_buffer[index] = 0; + + /* CLEAR THE NEXT ARGUMENT POINTER */ + arg_ptr[position] = NULL; + + /* STORE THE INTEGER VALUE OF EACH ARGUMENT PASSED*/ + index = 0; + while (arg_ptr[index] != NULL) { + //arg_value[index] = value_of(arg_ptr[index], TRUE); + + if ((resolved_integer = integer_resolve(arg_ptr[index])) != NULL) { + arg_value[index] = resolved_integer->value; + } else if ((resolved_cinteger = cinteger_resolve(arg_ptr[index])) != NULL) { + arg_value[index] = resolved_cinteger->value; + } else if (object_element_resolve(arg_ptr[index])) { + arg_value[index] = oec; + } else if ((counter = object_resolve(arg_ptr[index])) != -1) { + if (counter < 1 || counter > objects) { + badptrrun(arg_ptr[index], counter); + pop_stack(); + return; + } else { + arg_value[index] = counter; + } + } else if (validate(arg_ptr[index])) { + arg_value[index] = atoi(arg_ptr[index]); + } else { + arg_value[index] = -1; + } + + index++; + } + + /* THE CURRENT ARGUMENTS HAVE ALREADY BEEN PUSHED ONTO THE STACK + * AND STORED IF PASSED AS AN ARGUMENT TO THIS FUNCTION SO IT IS + * OKAY TO CLEAR THEM AND SET THE NEW VALUES */ + clear_cinteger("arg"); + clear_cstring("string_arg"); + + /* CREATE A CONSTANT FOR EACH ARGUMENT AFTER THE CORE FUNCTION NAME */ + index = 0; + while (arg_ptr[index] != NULL) { + if (index == 0) noun[3] = arg_value[index]; + add_cinteger("arg", arg_value[index]); + //printf("--- %s = %s\n", arg_ptr[index], arg_text_of(arg_ptr[index])); + add_cstring("string_arg", arg_text_of(arg_ptr[index])); + index++; + } +} + +void pop_stack() { + int index, counter; + + stack--; + + clear_cinteger("arg"); + clear_cstring("string_arg"); + + /* RECREATE THE arg ARRAY FOR THIS STACK FRAME */ + for (index = 0; index < backup[stack].argcount; index++) { + if (index == 0) noun[3] = backup[stack].arguments[0]; + add_cinteger("arg", backup[stack].arguments[index]); + } + + /* RECREATE THE string_arg ARRAY FOR THIS STACK FRAME */ + for (index = 0; index < backup[stack].argcount; index++) { + add_cstring("string_arg", backup[stack].str_arguments[index]); + } + + /* RESTORE THE CONTENTS OF text_buffer */ + for (counter = 0; counter < 1024; counter++) + text_buffer[counter] = backup[stack].text_buffer[counter]; + + /* RESTORE THE CONTENTS OF called_name */ + //for (counter = 0; counter < 256; counter++) + //called_name[counter] = backup[stack].called_name[counter]; + strncpy(called_name, backup[stack].called_name, 1024); + + /* RESTORE THE CONTENTS OF scope_criterion */ + //for (counter = 0; counter < 21; counter++) + // scope_criterion[counter] = backup[stack].scope_criterion[counter]; + strncpy(scope_criterion, backup[stack].scope_criterion, 20); + + /* RESTORE THE STORED FUNCTION NAMES THAT ARE USED WHEN AN + * 'override' COMMAND IS ENCOUNTERED IN THE CURRENT FUNCTION */ + strncpy(override, backup[stack].override, 80); + strncpy(default_function, backup[stack].default_function, 80); + + /* RESTORE ALL THE WORD POINTERS */ + for (counter = 0; counter < MAX_WORDS; counter++) { + word[counter] = backup[stack].word[counter]; + quoted[counter] = backup[stack].quoted[counter]; + } + + executing_function = backup[stack].function; + + if (executing_function != NULL) { + strncpy(function_name, executing_function->name, 80); + strncpy(cstring_resolve("function_name")->value, executing_function->name, 80); + } + + wp = backup[stack].wp; + top_of_loop = backup[stack].top_of_loop; + outfile = backup[stack].outfile; + infile = backup[stack].infile; + top_of_select = backup[stack].top_of_select; + top_of_while = backup[stack].top_of_while; + top_of_iterate = backup[stack].top_of_iterate; + top_of_update = backup[stack].top_of_update; + top_of_do_loop = backup[stack].top_of_do_loop; + criterion_value = backup[stack].criterion_value; + criterion_type = backup[stack].criterion_type; + criterion_negate = backup[stack].criterion_negate; + current_level = backup[stack].current_level; + execution_level = backup[stack].execution_level; + loop_integer = backup[stack].loop_integer; + select_integer = backup[stack].select_integer; + +#ifdef GLK + g_vm->glk_stream_set_position(game_stream, backup[stack].address, seekmode_Start); +#else + fseek(file, backup[stack].address, SEEK_SET); +#endif + +} + +void push_stack(int32 file_pointer) { + /* COPY ALL THE CURRENT SYSTEM DATA ONTO THE STACK */ + int index; + int counter = 0; + + if (stack == STACK_SIZE) { + log_error("Stack overflow.", PLUS_STDERR); + terminate(45); + } else { + backup[stack].infile = infile; + infile = NULL; + backup[stack].outfile = outfile; + outfile = NULL; + backup[stack].function = executing_function; + backup[stack].address = file_pointer; + backup[stack].wp = wp; + backup[stack].top_of_loop = top_of_loop; + backup[stack].top_of_select = top_of_select; + backup[stack].top_of_while = top_of_while; + backup[stack].top_of_iterate = top_of_iterate; + backup[stack].top_of_update = top_of_update; + backup[stack].top_of_do_loop = top_of_do_loop; + backup[stack].criterion_value = criterion_value; + backup[stack].criterion_type = criterion_type; + backup[stack].criterion_negate = criterion_negate; + backup[stack].current_level = current_level; + backup[stack].execution_level = execution_level; + backup[stack].loop_integer = loop_integer; + backup[stack].select_integer = select_integer; + + /* MAKE A COPY OF THE CURRENT CONTENTS OF text_buffer */ + for (counter = 0; counter < 1024; counter++) + backup[stack].text_buffer[counter] = text_buffer[counter]; + + /* MAKE A COPY OF THE CURRENT CONTENTS OF called_name */ + strncpy(backup[stack].called_name, called_name, 1024); + + // MAKE A COPY OF THE CURRENT CONTENTS OF scope_criterion + strncpy(backup[stack].scope_criterion, scope_criterion, 20); + + /* COPY THE STORED FUNCTION NAMES THAT ARE USED WHEN AN + * 'override' COMMAND IS ENCOUNTERED IN THE CURRENT FUNCTION */ + strncpy(backup[stack].override, override, 80); + strncpy(backup[stack].default_function, default_function, 80); + + /* PUSH ALL THE WORD POINTERS ONTO THE STACK */ + for (counter = 0; counter < MAX_WORDS; counter++) { + backup[stack].word[counter] = word[counter]; + backup[stack].quoted[counter] = quoted[counter]; + } + + // PUSH ALL THE ARGUMENTS AS INTEGERS ONTO THE STACK + index = 0; + current_cinteger = cinteger_table; + + if (current_cinteger != NULL) { + do { + if (!strcmp(current_cinteger->name, "arg")) { + backup[stack].arguments[index++] = current_cinteger->value; + } + current_cinteger = current_cinteger->next_cinteger; + } while (current_cinteger != NULL); + } + + // STORE THE NUMBER OF ARGUMENTS PASSED TO THIS FUNCTION + // THIS IS THE SAME NUMBER FOR STRINGS AND INTEGERS + backup[stack].argcount = index; + + // PUSH ALL THE ARGUMENTS AS STRINGS STRING ONTO THE STACK + index = 0; + current_cstring = cstring_table; + + if (current_cstring != NULL) { + do { + if (!strcmp(current_cstring->name, "string_arg")) { + strncpy(backup[stack].str_arguments[index++], current_cstring->value, 255); + } + + current_cstring = current_cstring->next_string; + } while (current_cstring != NULL); + } + } + + // PUSH ON TO THE NEXT STACK FRAME + stack++; +} + +void pop_proxy() { + int index, counter; + + proxy_stack--; + + clear_cinteger("$integer"); + clear_cstring("$string"); + clear_cstring("$word"); + + /* RECREATE THE integer ARRAY FOR THIS STACK FRAME */ + for (index = 0; index < proxy_backup[proxy_stack].integercount; index++) { + add_cinteger("$integer", proxy_backup[proxy_stack].integer[index]); + } + + /* RECREATE THE text ARRAY FOR THIS STACK FRAME */ + for (index = 0; index < proxy_backup[proxy_stack].textcount; index++) { + add_cstring("$string", proxy_backup[proxy_stack].text[index]); + } + + /* RECREATE THE $word ARRAY FOR THIS STACK FRAME */ + for (index = 0; index < proxy_backup[proxy_stack].commandcount; index++) { + add_cstring("$word", proxy_backup[proxy_stack].command[index]); + } + + /* RESTORE ALL THE NOUN POINTERS */ + for (counter = 0; counter < 4; counter++) + noun[counter] = proxy_backup[proxy_stack].object_pointers[counter]; + + /* PUSH ALL THE RESOLVED OBJECTS ONTO THE STACK */ + for (index = 0; index < 4; index++) { + list_size[index] = proxy_backup[proxy_stack].list_size[index]; + max_size[index] = proxy_backup[proxy_stack].max_size[index]; + for (counter = 0; counter < max_size[index]; counter++) { + object_list[index][counter] = proxy_backup[proxy_stack].object_list[index][counter]; + } + } + + start_of_this_command = proxy_backup[proxy_stack].start_of_this_command; + start_of_last_command = proxy_backup[proxy_stack].start_of_last_command; + after_from = proxy_backup[proxy_stack].after_from; + last_exact = proxy_backup[proxy_stack].last_exact; +} + +void push_proxy() { + /* COPY ALL THE CURRENT SYSTEM DATA ONTO THE STACK */ + int index; + int counter = 0; + int command = 0; + int text = 0; + + current_cinteger = cinteger_table; + current_cstring = cstring_table; + + if (proxy_stack == STACK_SIZE) { + log_error("Stack overflow.", PLUS_STDERR); + terminate(45); + } else { + proxy_backup[proxy_stack].start_of_this_command = start_of_this_command; + proxy_backup[proxy_stack].start_of_last_command = start_of_last_command; + + /* PUSH ALL THE OBJECT POINTERS ONTO THE STACK */ + for (counter = 0; counter < 4; counter++) + proxy_backup[proxy_stack].object_pointers[counter] = noun[counter]; + + /* PUSH ALL THE RESOLVED OBJECTS ONTO THE STACK */ + for (index = 0; index < 4; index++) { + for (counter = 0; counter < max_size[index]; counter++) { + proxy_backup[proxy_stack].object_list[index][counter] + = object_list[index][counter]; + } + proxy_backup[proxy_stack].list_size[index] = list_size[index]; + proxy_backup[proxy_stack].max_size[index] = max_size[index]; + } + + /* PUSH ALL THE CURRENT COMMAND INTEGERS ONTO THE STACK */ + counter = 0; + + if (current_cinteger != NULL) { + do { + if (!strcmp(current_cinteger->name, "$integer")) { + proxy_backup[proxy_stack].integer[counter++] = current_cinteger->value; + } + current_cinteger = current_cinteger->next_cinteger; + } while (current_cinteger != NULL); + } + + proxy_backup[proxy_stack].integercount = counter; + + // PUSH ALL THE TEXT STRING SUPPLIED BY THE CURRENT COMMAND ONTO THE STACK + text = 0; + command = 0; + + if (current_cstring != NULL) { + do { + if (!strcmp(current_cstring->name, "$string")) { + strncpy(proxy_backup[proxy_stack].text[text++], current_cstring->value, 255); + proxy_backup[proxy_stack].text[counter++][255] = 0; + } else if (!strcmp(current_cstring->name, "$word")) { + strncpy(proxy_backup[proxy_stack].command[command++], current_cstring->value, 255); + } + + current_cstring = current_cstring->next_string; + } while (current_cstring != NULL); + } + + proxy_backup[proxy_stack].textcount = counter; + proxy_backup[proxy_stack].commandcount = command; + proxy_backup[proxy_stack].after_from = after_from; + proxy_backup[proxy_stack].last_exact = last_exact; + } + + // PUSH ON TO THE NEXT STACK FRAME + proxy_stack++; +} + +int condition() { + /* COMPARE GROUPS OF TWO ELEMENTS. RETURN TRUE IF ANY ONE GROUP OF + * ELEMENTS COMPARE 'TRUE' */ + int first; + + first = 1; + + while (word[first + 2] != NULL && ((first + 2) < MAX_WORDS)) { + if (logic_test(first)) + return (TRUE); + else + first = first + 3; + } + return (FALSE); +} + +int and_condition() { + /* COMPARE GROUPS OF TWO ELEMENTS. RETURN FALSE IF ANY ONE GROUP OF + * ELEMENTS COMPARE 'FALSE' */ + int first; + + first = 1; + + while (word[first + 2] != NULL && ((first + 2) < MAX_WORDS)) { + if (logic_test(first) == FALSE) + return (FALSE); + else + first = first + 3; + } + return (TRUE); +} + +int logic_test(int first) { + long index, + compare; + + resolved_attribute = FALSE; + + index = value_of(word[first], TRUE); + compare = value_of(word[first + 2], TRUE); + + if (!strcmp(word[first + 1], "=") || !strcmp(word[first + 1], "==")) { + if (index == compare) + return (TRUE); + else + return (FALSE); + } else if (!strcmp(word[first + 1], ">")) { + if (index > compare) + return (TRUE); + else + return (FALSE); + } else if (!strcmp(word[first + 1], "<")) { + if (index < compare) + return (TRUE); + else + return (FALSE); + } else if (!strcmp(word[first + 1], "is")) { + if (index < 1 || index > objects) { + unkobjrun(first); + return (FALSE); + } else + return (scope(index, word[first + 2])); + } else if (!strcmp(word[first + 1], "isnt")) { + if (index < 1 || index > objects) { + unkobjrun(first); + return (FALSE); + } else + return (!scope(index, word[first + 2])); + } else if (!strcmp(word[first + 1], "has")) + if (index < 1 || index > objects) { + unkobjrun(first); + return (FALSE); + } else { + if (resolved_attribute == SYSTEM_ATTRIBUTE) { + return (object[index]->attributes & compare); + } else { + return (object[index]->user_attributes & compare); + } + } + else if (!strcmp(word[first + 1], "hasnt")) + if (index < 1 || index > objects) { + unkobjrun(first); + return (FALSE); + } else { + if (resolved_attribute == SYSTEM_ATTRIBUTE) { + return (!(object[index]->attributes & compare)); + } else { + return (!(object[index]->user_attributes & compare)); + } + } + else if (!strcmp(word[first + 1], "!=") + || !strcmp(word[first + 1], "<>")) { + if (index != compare) + return (TRUE); + else + return (FALSE); + } else if (!strcmp(word[first + 1], ">=") + || !strcmp(word[first + 1], "=>")) { + if (index >= compare) + return (TRUE); + else + return (FALSE); + } else if (!strcmp(word[first + 1], "<=") + || !strcmp(word[first + 1], "=<")) { + if (index <= compare) + return (TRUE); + else + return (FALSE); + } else if (!strcmp(word[first + 1], "grandof")) { + /* GRANDOF SAYS THAT AN OBJECT IS THE EVENTUAL PARENT OF ANOTHER OBJECT, NOT + * NECESSARILY IMMEDIATE */ + if (index < 1 || index > objects) { + unkobjrun(first); + return (FALSE); + } else { + if (compare < 1 || compare > objects) { + unkobjrun(first + 2); + return (FALSE); + } else { + if (parent_of(index, compare, UNRESTRICT)) + return (TRUE); + else + return (FALSE); + } + } + } else if (!strcmp(word[first + 1], "!grandof")) { + if (index < 1 || index > objects) { + unkobjrun(first); + return (FALSE); + } else { + if (compare < 1 || compare > objects) { + unkobjrun(first + 2); + return (FALSE); + } else { + if (parent_of(index, compare, UNRESTRICT)) + return (FALSE); + else + return (TRUE); + } + } + } else { + sprintf(error_buffer, + "ERROR: In function \"%s\", illegal operator \"%s\".^", + executing_function->name, word[2]); + write_text(error_buffer); + return (FALSE); + } +} + +int strcondition() { + int first; + + first = 1; + + while (word[first + 2] != NULL && ((first + 2) < MAX_WORDS)) { + if (str_test(first)) + return (TRUE); + else + first = first + 3; + } + return (FALSE); +} + +int and_strcondition() { + int first; + + first = 1; + + while (word[first + 2] != NULL && ((first + 2) < MAX_WORDS)) { + if (str_test(first) == FALSE) + return (FALSE); + else + first = first + 3; + } + return (TRUE); +} + +int str_test(int first) { + char *index; + char *compare; + + // GET THE TWO STRING VALUES TO COMPARE + + index = arg_text_of_word(first); + compare = arg_text_of_word(first + 2); + + if (!strcmp(word[first + 1], "==") || !strcmp(word[first + 1], "=")) { + if (!scumm_stricmp(index, compare)) { + return (TRUE); + } else { + return (FALSE); + } + } else if (!strcmp(word[first + 1], "!contains")) { + if (strcasestr(index, compare)) + return (FALSE); + else + return (TRUE); + } else if (!strcmp(word[first + 1], "contains")) { + if (strcasestr(index, compare)) + return (TRUE); + else + return (FALSE); + } else if (!strcmp(word[first + 1], "<>") || !strcmp(word[first + 1], "!=")) { + if (scumm_stricmp(index, compare)) + return (TRUE); + else + return (FALSE); + } else if (!strcmp(word[first + 1], "==C") || !strcmp(word[first + 1], "=C")) { + if (!strcmp(index, compare)) { + return (TRUE); + } else { + return (FALSE); + } + } else if (!strcmp(word[first + 1], "!containsC")) { + if (strstr(index, compare)) + return (FALSE); + else + return (TRUE); + } else if (!strcmp(word[first + 1], "containsC")) { + if (strstr(index, compare)) + return (TRUE); + else + return (FALSE); + } else if (!strcmp(word[first + 1], "<>C") || !strcmp(word[first + 1], "!=C")) { + if (strcmp(index, compare)) + return (TRUE); + else + return (FALSE); + } else { + sprintf(error_buffer, + "ERROR: In function \"%s\", illegal operator \"%s\".^", + executing_function->name, word[2]); + write_text(error_buffer); + return (FALSE); + } +} + +void add_cinteger(char *name, int value) { + /* THIS FUNCTION ADDS A NEW JACL CONSTANT TO THE LIST */ + + if ((new_cinteger = (struct cinteger_type *) + malloc(sizeof(struct cinteger_type))) == NULL) + outofmem(); + else { + if (cinteger_table == NULL) { + cinteger_table = new_cinteger; + } else { + /* FIND LAST CONSTANT IN LIST */ + current_cinteger = cinteger_table; + while (current_cinteger->next_cinteger != NULL) { + current_cinteger = current_cinteger->next_cinteger; + } + current_cinteger->next_cinteger = new_cinteger; + } + strncpy(new_cinteger->name, name, 40); + new_cinteger->name[40] = 0; + new_cinteger->value = value; + new_cinteger->next_cinteger = NULL; + } +} + +void clear_cinteger(char *name) { + /* FREE CONSTANTS THAT HAVE SUPPLIED NAME*/ + + //printf("--- clear integer %s\n", name); + if (cinteger_table != NULL) { + current_cinteger = cinteger_table; + previous_cinteger = cinteger_table; + while (current_cinteger != NULL) { + //sprintf(temp_buffer, "--- checking integer %s^", current_cinteger->name); + //write_text(temp_buffer); + if (!strcmp(current_cinteger->name, name)) { + //sprintf(temp_buffer, "--- found integer %s^", name); + //write_text(temp_buffer); + /* FREE THIS CONSTANT */ + if (previous_cinteger == current_cinteger) { + // THE INTEGER BEING CLEARED IS THE FIRST INTEGER IN THE LIST + cinteger_table = current_cinteger->next_cinteger; + previous_cinteger = current_cinteger->next_cinteger; + free(current_cinteger); + current_cinteger = previous_cinteger; + } else { + previous_cinteger->next_cinteger = current_cinteger->next_cinteger; + free(current_cinteger); + current_cinteger = previous_cinteger->next_cinteger; + } + } else { + previous_cinteger = current_cinteger; + current_cinteger = current_cinteger->next_cinteger; + } + } + } + //printf("--- leaving clear integer\n"); +} + +void add_cstring(char *name, char *value) { + /* ADD A STRING CONSTANT WITH THE SUPPLIED NAME AND VALUE */ + + if ((new_string = (struct string_type *) + malloc(sizeof(struct string_type))) == NULL) + outofmem(); + else { + if (cstring_table == NULL) { + cstring_table = new_string; + } else { + /* FIND LAST STRING IN LIST */ + current_cstring = cstring_table; + while (current_cstring->next_string != NULL) { + current_cstring = current_cstring->next_string; + } + current_cstring->next_string = new_string; + } + strncpy(new_string->name, name, 40); + new_string->name[40] = 0; + strncpy(new_string->value, value, 255); + new_string->value[255] = 0; + new_string->next_string = NULL; + } +} + +void clear_cstring(char *name) { + /* FREE CONSTANTS THAT HAVE SUPPLIED NAME*/ + if (cstring_table != NULL) { + current_cstring = cstring_table; + previous_cstring = cstring_table; + while (current_cstring != NULL) { + if (!strcmp(current_cstring->name, name)) { + /* FREE THIS STRING */ + if (previous_cstring == current_cstring) { + cstring_table = current_cstring->next_string; + previous_cstring = current_cstring->next_string; + free(current_cstring); + current_cstring = previous_cstring; + } else { + previous_cstring->next_string = current_cstring->next_string; + free(current_cstring); + current_cstring = previous_cstring->next_string; + } + } else { + previous_cstring = current_cstring; + current_cstring = current_cstring->next_string; + } + } + } +} + +void inspect(int object_num) { + // THIS FUNCTION DISPLAYS THE STATE OF A JACL OBJECT FOR DEBUGGING + + int index, attribute_value; + + struct attribute_type *pointer = attribute_table; + + if (object_num < 1 || object_num > objects) { + badptrrun(word[1], object_num); + return; + } + + write_text("label: "); + write_text(object[object_num]->label); + + if (object[object_num]->attributes & LOCATION) { + // OUTPUT ALL THE ATTRIBUTES WITH LOCATION ATTRIBUTE TEXT + write_text("^has location attributes: "); + index = 0; + attribute_value = 1; + while (location_attributes[index] != NULL) { + if (object[object_num]->attributes & attribute_value) { + write_text(location_attributes[index]); + } + index++; + attribute_value *= 2; + } + } else { + // OUTPUT ALL THE ATTRIBUTES WITH OBJECT ATTRIBUTE TEXT + write_text("^has object attributes: "); + index = 0; + attribute_value = 1; + while (object_attributes[index] != NULL) { + if (object[object_num]->attributes & attribute_value) { + write_text(object_attributes[index]); + } + index++; + attribute_value *= 2; + } + + write_text("^has user attributes: "); + attribute_value = 1; + } + + if (pointer != NULL) { + // THERE ARE USER ATTRIBUTES, SO CHECK IF THIS OBJECT OR LOCATION + // HAS ANY OF THEM + do { + if (object[object_num]->user_attributes & pointer->value) { + write_text(pointer->name); + write_text(" "); + } + + pointer = pointer->next_attribute; + } while (pointer != NULL); + } + + write_text("^"); + + index = 0; + if (object[object_num]->attributes & LOCATION) { + while (location_elements[index] != NULL) { + if (index < 12) { + if (object[object_num]->integer[index] < 1 || object[object_num]->integer[index] > objects) { + sprintf(temp_buffer, "%s: nowhere (%d)^", location_elements[index], object[object_num]->integer[index]); + } else { + sprintf(temp_buffer, "%s: %s (%d)^", location_elements[index], object[object[object_num]->integer[index]]->label, object[object_num]->integer[index]); + } + } else { + sprintf(temp_buffer, "%s: %d^", location_elements[index], object[object_num]->integer[index]); + } + write_text(temp_buffer); + index++; + } + } else { + while (object_elements[index] != NULL) { + if (index == 0) { + sprintf(temp_buffer, "%s: %s (%d)^", object_elements[index], object[object[object_num]->integer[index]]->label, object[object_num]->integer[index]); + } else { + sprintf(temp_buffer, "%s: %d^", object_elements[index], object[object_num]->integer[index]); + } + write_text(temp_buffer); + index++; + } + } +} + +int grand_of(int child, int objs_only) { + /* THIS FUNCTION WILL CLIMB THE OBJECT TREE STARTING AT 'CHILD' UNTIL + * A 'PARENT' IS REACHED */ + + /* objs_only ARGUMENT TELLS FUNCTION TO IGNORE OBJECT IF IT IS IN A + * LOCATION */ + + int parent; + + if (object[child]->PARENT != NOWHERE) { + /* STORE THE CHILDS PARENT OBJECT */ + parent = object[child]->PARENT; + + if (object[parent]->attributes & LOCATION) { + if (objs_only) { + /* THE CHILDS PARENT IS LOCATION AND SEARCH IS RESTRICTED TO + * OBJECTS */ + return (child); + } else { + return (parent); + } + } else { + /* KEEP LOOKING UP THE TREE UNTIL THE CHILD HAS NO + * PARENT */ + return (grand_of(parent, objs_only)); + } + } else { + /* THE SPECIFIED OBJECT HAS NO PARENT */ + return (child); + } +} + +int select_next() { + while (++*select_integer <= objects) { + switch (criterion_type) { + case CRI_ATTRIBUTE: + if (object[*select_integer]->attributes & criterion_value) { + if (!criterion_negate) { + return TRUE; + } + } else { + if (criterion_negate) { + return TRUE; + } + } + break; + case CRI_USER_ATTRIBUTE: + if (object[*select_integer]->user_attributes & criterion_value) { + if (!criterion_negate) { + return TRUE; + } + } else { + if (criterion_negate) { + return TRUE; + } + } + break; + case CRI_PARENT: + if (object[*select_integer]->PARENT == criterion_value) { + if (!criterion_negate) { + return TRUE; + } + } else { + if (criterion_negate) { + return TRUE; + } + } + break; + case CRI_SCOPE: + if (scope(*select_integer, scope_criterion)) { + if (!criterion_negate) { + return TRUE; + } + } else { + if (criterion_negate) { + return TRUE; + } + } + break; + } + } + + return (FALSE); +} + +/* Converts an integer value to its hex character*/ +char to_hex(char code) { + static char hex[] = "0123456789abcdef"; + return hex[code & 15]; +} + +/* Returns a url-encoded version of str */ +/* IMPORTANT: be sure to free() the returned string after use */ +char *url_encode(char *str) { + char *pstr = str, *buf = (char *)malloc(strlen(str) * 3 + 1), *pbuf = buf; + while (*pstr) { + if (Common::isAlnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') + *pbuf++ = *pstr; + else if (*pstr == ' ') + *pbuf++ = '+'; + else + *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15); + pstr++; + } + *pbuf = '\0'; + return buf; +} + +} // End of namespace JACL +} // End of namespace Glk |