aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/jacl/interpreter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/jacl/interpreter.cpp')
-rw-r--r--engines/glk/jacl/interpreter.cpp3458
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&amp;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&amp;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, "&amp;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