1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "glk/jacl/jacl.h"
24 #include "glk/jacl/language.h"
25 #include "glk/jacl/types.h"
26 #include "glk/jacl/prototypes.h"
27 #include "glk/jacl/csv.h"
28 #include "common/str.h"
29
30 namespace Glk {
31 namespace JACL {
32
33 struct flock {
34 short l_type;
35 short l_whence;
36 long l_start;
37 long l_len;
38 long l_pid;
39 };
40
41 #define F_DUPFD 0
42 #define F_GETFD 1
43 #define F_SETFD 2
44 #define F_GETFL 3
45 #define F_SETFL 4
46 #define F_GETLK 5
47 #define F_SETLK 6
48 #define F_SETLKW 7
49
50 #define F_RDLCK 0
51 #define F_WRLCK 1
52 #define F_UNLCK 2
53
fcntl(int __fd,int __cmd,...)54 int fcntl(int __fd, int __cmd, ...) {
55 return 0;
56 }
57
58 #ifndef strcasestr
strcasestr(const char * s,const char * find)59 const char *strcasestr(const char *s, const char *find) {
60 char c, sc;
61 size_t len;
62
63 if ((c = *find++) != 0) {
64 c = (char)tolower((unsigned char)c);
65 len = strlen(find);
66 do {
67 do {
68 if ((sc = *s++) == 0)
69 return (NULL);
70 } while ((char)tolower((unsigned char)sc) != c);
71 } while (scumm_strnicmp(s, find, len) != 0);
72 s--;
73 }
74 return s;
75 }
76 #endif
77
78 #define MAX_TRY 10
79
80 flock read_lck;
81 int read_fd;
82 flock write_lck;
83 int write_fd;
84
85 char *url_encode(char *str);
86 char to_hex(char code);
87
88 const char *location_attributes[] = {
89 "VISITED ", "DARK ", "ON_WATER ", "UNDER_WATER ", "WITHOUT_AIR ", "OUTDOORS ",
90 "MID_AIR ", "TIGHT_ROPE ", "POLLUTED ", "SOLVED ", "MID_WATER ", "DARKNESS ",
91 "MAPPED ", "KNOWN ",
92 NULL
93 };
94
95 const char *object_attributes[] = {
96 "CLOSED ", "LOCKED ", "DEAD ", "IGNITABLE ", "WORN ", "CONCEALING ",
97 "LUMINOUS ", "WEARABLE ", "CLOSABLE ", "LOCKABLE ", "ANIMATE ", "LIQUID ",
98 "CONTAINER ", "SURFACE ", "PLURAL ", "FLAMMABLE ", "BURNING ", "LOCATION ",
99 "ON ", "DAMAGED ", "FEMALE ", "POSSESSIVE ", "OUT_OF_REACH ", "TOUCHED ",
100 "SCORED ", "SITTING ", "NPC ", "DONE ", "GAS ", "NO_TAB ",
101 "NOT_IMPORTANT ", NULL
102 };
103
104 const char *object_elements[] = {
105 "parent", "capacity", "mass", "bearing", "velocity", "next", "previous",
106 "child", "index", "status", "state", "counter", "points", "class", "x", "y",
107 NULL
108 };
109
110 const char *location_elements[] = {
111 "north", "south", "east", "west", "northeast", "northwest", "southeast",
112 "southwest", "up", "down", "in", "out", "points", "class", "x", "y",
113 NULL
114 };
115
116 struct csv_parser parser_csv;
117 char in_name[1024];
118 char out_name[1024];
119 Common::SeekableReadStream *infile;
120 Common::WriteStream *outfile;
121
122 int stack = 0;
123 int proxy_stack = 0;
124
125 int field_no = 0;
126
127 struct stack_type backup[STACK_SIZE];
128 struct proxy_type proxy_backup[STACK_SIZE];
129
130 struct function_type *resolved_function = NULL;
131 struct string_type *resolved_string = NULL;
132
133 struct string_type *new_string = NULL;
134 struct string_type *current_cstring = NULL;
135 struct string_type *previous_cstring = NULL;
136
137 struct cinteger_type *new_cinteger = NULL;
138 struct cinteger_type *current_cinteger = NULL;
139 struct cinteger_type *previous_cinteger = NULL;
140
141 long bit_mask;
142 extern int encrypted;
143 extern int after_from;
144 extern int last_exact;
145
146 extern char temp_directory[];
147 extern char data_directory[];
148 char csv_buffer[1024];
149
150 int resolved_attribute;
151
152 /* THE ITERATION VARIABLE USED FOR LOOPS */
153 int *loop_integer = NULL;
154 int *select_integer = NULL;
155
156 int criterion_value = 0;
157 int criterion_type = 0;
158 int criterion_negate = FALSE;
159 int current_level;
160 int execution_level;
161 int *ask_integer;
162 int new_x;
163 int new_y;
164
165 int interrupted = FALSE;
166 char string_buffer[2048];
167 char argument_buffer[1024];
168 #ifdef GLK
169 extern schanid_t sound_channel[];
170 extern strid_t game_stream;
171 extern winid_t mainwin;
172 extern winid_t statuswin;
173 extern winid_t current_window;
174
175 extern strid_t mainstr;
176 extern strid_t statusstr;
177 extern strid_t quotestr;
178 extern strid_t inputstr;
179 int top_of_loop = 0;
180 int top_of_select = 0;
181 int top_of_while = 0;
182 int top_of_iterate = 0;
183 int top_of_update = 0;
184 int top_of_do_loop = 0;
185 #else
186 extern FILE *file;
187 char option_buffer[2024];
188 int style_stack[100];
189 int style_index = 0;
190 long top_of_loop = 0;
191 long top_of_select = 0;
192 long top_of_while = 0;
193 long top_of_iterate = 0;
194 long top_of_update = 0;
195 long top_of_do_loop = 0;
196
197 #endif
198
199 #ifdef __NDS__
200 extern int bold_mode;
201 extern int pre_mode;
202 extern int reverse_mode;
203 extern int input_mode;
204 extern int subheader_mode;
205 extern int note_mode;
206 #endif
207
208 extern char user_id[];
209 extern char prefix[];
210 extern char text_buffer[];
211 extern char chunk_buffer[];
212 extern const char *word[];
213
214 extern char bookmark[];
215 extern char file_prompt[];
216
217 /* CONTAINED IN PARSER.C */
218 extern int object_list[4][MAX_OBJECTS];
219 extern int list_size[];
220 extern int max_size[];
221
222 /* CONTAINED IN ENCAPSULATE.C */
223 extern int quoted[];
224
225 extern struct object_type *object[];
226 extern struct integer_type *integer_table;
227 extern struct integer_type *integer[];
228 extern struct cinteger_type *cinteger_table;
229 extern struct attribute_type *attribute_table;
230 extern struct string_type *string_table;
231 extern struct string_type *cstring_table;
232 extern struct function_type *function_table;
233 extern struct function_type *executing_function;
234 extern struct command_type *completion_list;
235 extern struct word_type *grammar_table;
236 extern struct synonym_type *synonym_table;
237 extern struct filter_type *filter_table;
238
239 extern char function_name[];
240 extern char temp_buffer[];
241 extern char error_buffer[];
242 extern char proxy_buffer[];
243
244 extern char default_function[];
245 extern char override_[];
246
247 extern int noun[];
248 extern int wp;
249 extern int start_of_this_command;
250 extern int start_of_last_command;
251 extern int buffer_index;
252 extern int objects;
253 extern int integers;
254 extern int player;
255 extern int oec;
256 extern int *object_element_address;
257 extern int *object_backup_address;
258 extern int walkthru_running;
259
260 // VALUES FROM LOADER
261 extern int value_resolved;
262
263 extern Common::WriteStream *transcript;
264 extern char margin_string[];
265
266 char integer_buffer[16];
267 char called_name[1024];
268 char scope_criterion[24];
269 const char *output;
270
terminate(int code)271 void terminate(int code) {
272 // FREE ANY EXTRA RAM ALLOCATED BY THE CSV PARSER
273 csv_free(&parser_csv);
274
275 #ifdef GLK
276 int index;
277 event_t event;
278
279 // FLUSH THE GLK WINDOW SO THE ERROR GETS DISPLAYED IMMEDIATELY.
280 g_vm->glk_select_poll(&event);
281
282 /* CLOSE THE SOUND CHANNELS */
283 for (index = 0; index < 8; index++) {
284 if (sound_channel[index] != NULL) {
285 g_vm->glk_schannel_destroy(sound_channel[index]);
286 }
287 }
288
289 /* CLOSE THE STREAM */
290 if (game_stream != NULL) {
291 g_vm->glk_stream_close(game_stream, NULL);
292 }
293
294 g_vm->glk_exit();
295 #else
296 if (file != NULL) /* CLOSE THE GAME FILE */
297 fclose(file);
298
299 exit(code);
300 #endif
301 }
302
build_proxy()303 void build_proxy() {
304 int index;
305
306 proxy_buffer[0] = 0;
307
308 /* LOOP THROUGH ALL THE PARAMETERS OF THE PROXY COMMAND
309 AND BUILD THE MOVE TO BE ISSUED ON THE PLAYER'S BEHALF */
310 for (index = 1; word[index] != NULL; index++) {
311 strcat(proxy_buffer, text_of_word(index));
312 }
313
314 for (index = 0; index < (int)strlen(proxy_buffer); index++) {
315 if (proxy_buffer[index] == '~') {
316 proxy_buffer[index] = '\"';
317 }
318 }
319
320 //printf("--- proxy buffer = \"%s\"\n", proxy_buffer);
321 }
322
cb1(void * s,size_t i,void * not_used)323 void cb1(void *s, size_t i, void *not_used) {
324 struct string_type *resolved_cstring;
325
326 //sprintf (temp_buffer, "Trying to set field %d to equal %s^", field_no, (const char *) s);
327 //write_text(temp_buffer);
328
329 sprintf(temp_buffer, "field[%d]", field_no);
330
331 if ((resolved_cstring = cstring_resolve(temp_buffer)) != NULL) {
332 //write_text("Resolved ");
333 //write_text(temp_buffer);
334 //write_text("^");
335 strncpy(resolved_cstring->value, (const char *)s, i);
336 resolved_cstring->value[i] = 0;
337 //sprintf(temp_buffer, "Setting field %d to ~%s~^", field_no, (const char *) s);
338 //write_text(temp_buffer);
339 // INCREMENT THE FIELD NUMBER SO THE NEXT ONE GETS STORED IN THE RIGHT CONSTANT
340 field_no++;
341 } else {
342 write_text("Can't resolve ");
343 write_text(temp_buffer);
344 write_text("^");
345 }
346
347 }
348
cb2(int c,void * not_used)349 void cb2(int c, void *not_used) {
350 // THE END OF THE RECORD HAS BEEN REACHED, EXPORT THE NUMBER OF FIELDS READ
351 struct cinteger_type *resolved_cinteger;
352
353 if ((resolved_cinteger = cinteger_resolve("field_count")) != NULL) {
354 resolved_cinteger->value = field_no;
355 }
356 }
357
execute(const char * funcname)358 int execute(const char *funcname) {
359 int index;
360 int counter;
361 int *container;
362
363 int object_1,
364 object_2;
365
366 if (g_vm->shouldQuit())
367 return 0;
368
369 /* THESE VARIABLE KEEP TRACK OF if AND endif COMMANDS TO DECIDE WHETHER
370 *THE CURRENT LINE OF CODE SHOULD BE EXECUTED OR NOT */
371 int currentLevel = 0;
372 int executionLevel = 0;
373
374 /* THESE ARE USED AS FILE POINTER OFFSETS TO RETURN TO FIXED
375 * POINTS IN THE GAME FILE */
376 #ifdef GLK
377 int before_command = 0;
378 #else
379 long before_command = 0;
380 #endif
381
382
383 strncpy(called_name, funcname, 1023);
384
385 /* GET THE FUNCTION OBJECT BY THE FUNCTION NAME */
386 resolved_function = function_resolve(called_name);
387
388 if (resolved_function == NULL) {
389 //printf("--- failed to find %s\n", called_name);
390 return (FALSE);
391 }
392
393 #ifdef GLK
394 push_stack(g_vm->glk_stream_get_position(game_stream));
395 if (g_vm->shouldQuit())
396 return FALSE;
397 #else
398 push_stack(ftell(file));
399 #endif
400
401 top_of_loop = 0;
402 top_of_select = 0;
403 top_of_while = 0;
404 top_of_iterate = 0;
405 top_of_update = 0;
406 top_of_do_loop = 0;
407
408 executing_function = resolved_function;
409 executing_function->call_count++;
410
411 // CREATE ALL THE PASSED ARGUMENTS AS JACL INTEGER CONSTANTS
412 set_arguments(called_name);
413
414 // SET function_name TO THE CORE NAME STORED IN THE FUNCTION OBJECT
415 // LEAVING called_name TO CONTAIN THE FULL ARGUMENT LIST
416 strncpy(function_name, executing_function->name, 80);
417 strncpy(cstring_resolve("function_name")->value, executing_function->name, 80);
418
419 //sprintf(temp_buffer, "--- starting to execute %s^", function_name);
420 //write_text(temp_buffer);
421
422 // JUMP TO THE POINT IN THE PROCESSED GAME FILE WHERE THIS FUNCTION STARTS
423 #ifdef GLK
424 g_vm->glk_stream_set_position(game_stream, executing_function->position, seekmode_Start);
425 before_command = executing_function->position;
426 (void)glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
427 #else
428 fseek(file, executing_function->position, SEEK_SET);
429 before_command = executing_function->position;
430 fgets(text_buffer, 1024, file);
431 #endif
432
433 if (encrypted) jacl_decrypt(text_buffer);
434
435 while (text_buffer[0] != 125 && !interrupted) {
436 encapsulate();
437 if (word[0] == NULL);
438 else if (!strcmp(word[0], "endwhile")) {
439 currentLevel--;
440 if (currentLevel < executionLevel) {
441 // THIS ENDWHILE COMMAND WAS BEING EXECUTED,
442 // NOT JUST COUNTED.
443 if (top_of_while == FALSE) {
444 sprintf(error_buffer, NO_WHILE, executing_function->name);
445 log_error(error_buffer, PLUS_STDOUT);
446 } else {
447 #ifdef GLK
448 g_vm->glk_stream_set_position(game_stream, top_of_while, seekmode_Start);
449 #else
450 fseek(file, top_of_while, SEEK_SET);
451 #endif
452 executionLevel = currentLevel;
453 }
454 }
455 } else if (!strcmp(word[0], "enditerate")) {
456 currentLevel--;
457 if (currentLevel < executionLevel) {
458 // THIS ENDITERATE COMMAND WAS BEING EXECUTED,
459 // NOT JUST COUNTED.
460 if (top_of_iterate == FALSE) {
461 sprintf(error_buffer, NO_ITERATE, executing_function->name);
462 log_error(error_buffer, PLUS_STDOUT);
463 } else {
464 #ifdef GLK
465 g_vm->glk_stream_set_position(game_stream, top_of_iterate, seekmode_Start);
466 #else
467 fseek(file, top_of_iterate, SEEK_SET);
468 #endif
469 executionLevel = currentLevel;
470 }
471 }
472 } else if (!strcmp(word[0], "endupdate")) {
473 currentLevel--;
474 if (currentLevel < executionLevel) {
475 // THIS ENDUPDATE COMMAND WAS BEING EXECUTED,
476 // NOT JUST COUNTED.
477 if (top_of_update == FALSE) {
478 sprintf(error_buffer, NO_UPDATE, executing_function->name);
479 log_error(error_buffer, PLUS_STDOUT);
480 } else {
481 #ifdef GLK
482 g_vm->glk_stream_set_position(game_stream, top_of_update, seekmode_Start);
483 #else
484 fseek(file, top_of_update, SEEK_SET);
485 #endif
486 executionLevel = currentLevel;
487 }
488 }
489 } else if (!strcmp(word[0], "print") && currentLevel != executionLevel) {
490 // SKIP THIS BLOCK OF PLAIN TEXT UNTIL IT FINDS A
491 // LINE THAT STARTS WITH A '.' OR A '}'
492 #ifdef GLK
493 (void)glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
494 #else
495 fgets(text_buffer, 1024, file);
496 #endif
497
498 if (encrypted) jacl_decrypt(text_buffer);
499
500 while (text_buffer[0] != '.') {
501 if (text_buffer[0] == '}') {
502 // HIT THE END OF THE FUNCTION, JUST BAIL OUT
503 return (exit_function(TRUE));
504 }
505
506 // GET THE NEXT LINE
507 #ifdef GLK
508 (void)glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
509 #else
510 fgets(text_buffer, 1024, file);
511 #endif
512
513 if (encrypted) jacl_decrypt(text_buffer);
514
515 }
516 } else if (!strcmp(word[0], "endif")) {
517 currentLevel--;
518 if (currentLevel < executionLevel) {
519 /* THIS SHOULD NEVER HAPPEN */
520 executionLevel = currentLevel;
521 }
522 } else if (!strcmp(word[0], "endall")) {
523 currentLevel = 0;
524 executionLevel = 0;
525 } else if (!strcmp(word[0], "else")) {
526 if (currentLevel == executionLevel) {
527 executionLevel--;
528 } else if (currentLevel == executionLevel + 1) {
529 executionLevel++;
530 }
531 } else if (currentLevel == executionLevel) {
532 if (!strcmp(word[0], "look")) {
533 // THIS IS JUST HERE FOR BACKWARDS COMPATIBILITY
534 object[HERE]->attributes &= ~1L;
535 look_around();
536 } else if (!strcmp(word[0], "repeat")) {
537 #ifdef GLK
538 top_of_do_loop = g_vm->glk_stream_get_position(game_stream);
539 #else
540 top_of_do_loop = ftell(file);
541 #endif
542 } else if (!strcmp(word[0], "until")) {
543 if (word[3] == NULL) {
544 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
545 noproprun();
546 return (exit_function(TRUE));
547 } else {
548 if (top_of_do_loop == FALSE) {
549 sprintf(error_buffer, NO_REPEAT, executing_function->name);
550 log_error(error_buffer, PLUS_STDOUT);
551 } else if (!condition()) {
552 #ifdef GLK
553 g_vm->glk_stream_set_position(game_stream, top_of_do_loop, seekmode_Start);
554 #else
555 fseek(file, top_of_do_loop, SEEK_SET);
556 #endif
557 }
558 }
559 } else if (!strcmp(word[0], "untilall")) {
560 if (word[3] == NULL) {
561 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
562 noproprun();
563 return (exit_function(TRUE));
564 } else {
565 if (top_of_do_loop == FALSE) {
566 sprintf(error_buffer, NO_REPEAT, executing_function->name);
567 log_error(error_buffer, PLUS_STDOUT);
568 } else if (!and_condition()) {
569 #ifdef GLK
570 g_vm->glk_stream_set_position(game_stream, top_of_do_loop, seekmode_Start);
571 #else
572 fseek(file, top_of_do_loop, SEEK_SET);
573 #endif
574
575 }
576 }
577 } else if (!strcmp(word[0], "iterate")) {
578 int i;
579
580 // A NEW iterate LOOP MEANS STARTING BACK AT THE FIRST FIELD
581 field_no = 0;
582
583 currentLevel++;
584 /* THIS LOOP COMES BACK TO THE START OF THE LINE CURRENTLY
585 EXECUTING, NOT THE LINE AFTER */
586
587 top_of_iterate = before_command;
588
589 // infile REMAINS OPEN DURING THE ITERATION, ONLY NEEDS
590 // OPENING THE FIRST TIME
591 if (infile == NULL) {
592 strcpy(temp_buffer, data_directory);
593 strcat(temp_buffer, prefix);
594 strcat(temp_buffer, "-");
595 strcat(temp_buffer, text_of_word(1));
596 strcat(temp_buffer, ".csv");
597
598 infile = File::openForReading(temp_buffer);
599
600 if (word[2] != NULL && !strcmp(word[2], "skip_header")) {
601 assert(infile);
602 infile->read(csv_buffer, 1024);
603 }
604 }
605
606 if (infile == NULL) {
607 sprintf(error_buffer, "Failed to open file %s\n", temp_buffer);
608 log_error(error_buffer, LOG_ONLY);
609 infile = NULL;
610 } else {
611 if (word[1] == NULL) {
612 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
613 noproprun();
614 return (exit_function(TRUE));
615 } else {
616 // IF THERE IS ANOTHER RECORD TO READ FROM THE CSV FILE THEN
617 // SET THE field[] CONSTANTS AND INCREMENT THE executionLevel
618 infile->read(csv_buffer, 1024);
619
620 if (infile->pos() < infile->size()) {
621 i = strlen(csv_buffer);
622 //sprintf (temp_buffer, "Read ~%s~ with %d bytes.^", csv_buffer, i);
623 //write_text(temp_buffer);
624 if (csv_parse(&parser_csv, csv_buffer, i, cb1, cb2, (void *) NULL) != (uint)i) {
625 sprintf(error_buffer, "Error parsing file: %s\n", csv_strerror(csv_error(&parser_csv)));
626 log_error(error_buffer, PLUS_STDOUT);
627 delete infile;
628 infile = NULL;
629 } else {
630 // A LINE HAS BEEN SUCCESSFULLY READ, EXECUTE THE CONTENTS OF THE LOOP
631 executionLevel++;
632 }
633 } else {
634 delete infile;
635 infile = NULL;
636 }
637 }
638 }
639 } else if (!strcmp(word[0], "update")) {
640 int i;
641
642 // SET UP THE RECORD LOCKING STRUCTURE, THE ADDRESS OF WHICH
643 // IS PASSED TO THE fcntl() SYSTEM CALL
644 write_lck.l_type = F_WRLCK; // SETTING A WRITE LOCK
645 write_lck.l_whence = 0; // OFFSET l_start FROM BEGINNING OF FILE
646 write_lck.l_start = 0LL;
647 write_lck.l_len = 0LL; // UNTIL THE END OF THE FILE ADDRESS SPACE
648
649 read_lck.l_type = F_RDLCK; // SETTING A READ LOCK
650 read_lck.l_whence = 0; // OFFSET l_start FROM BEGINNING OF FILE
651 read_lck.l_start = 0LL;
652 read_lck.l_len = 0LL; // UNTIL THE END OF THE FILE ADDRESS SPACE
653
654 // A NEW iterate LOOP MEANS STARTING BACK AT THE FIRST FIELD
655 field_no = 0;
656
657 currentLevel++;
658 // THIS LOOP COMES BACK TO THE START OF THE LINE CURRENTLY
659 // EXECUTING, NOT THE LINE AFTER
660
661 top_of_update = before_command;
662
663 // infile REMAINS OPEN DURING THE ITERATION, ONLY NEEDS
664 // OPENING THE FIRST TIME
665 if (infile == NULL) {
666 strcpy(in_name, data_directory);
667 strcat(in_name, prefix);
668 strcat(in_name, "-");
669 strcat(in_name, text_of_word(1));
670 strcat(in_name, ".csv");
671
672 infile = File::openForReading(in_name);
673 }
674
675 if (outfile == NULL) {
676 // OPEN A TEMPORARY OUTPUT FILE TO WRITE THE MODIFICATIONS TO
677 strcpy(out_name, data_directory);
678 strcat(out_name, prefix);
679 strcat(out_name, "-");
680 strcat(out_name, text_of_word(1));
681 strcat(out_name, "-");
682 strcat(out_name, user_id);
683 strcat(out_name, ".csv");
684
685 outfile = File::openForWriting(out_name);
686 }
687
688 if (infile == NULL) {
689 sprintf(error_buffer, "Failed to open input CSV file ~%s\n", in_name);
690 log_error(error_buffer, LOG_ONLY);
691 if (outfile != NULL) {
692 delete outfile;
693 outfile = NULL;
694 }
695 return (exit_function(TRUE));
696 } else {
697 if (outfile == NULL) {
698 sprintf(error_buffer, "Failed to open output CSV file ~%s~\n", out_name);
699 log_error(error_buffer, LOG_ONLY);
700 if (infile != NULL) {
701 delete infile;
702 infile = NULL;
703 }
704 return (exit_function(TRUE));
705 } else {
706 #ifdef FILE_CTL
707 int tryCtr = 0;
708 write_fd = fileno(outfile);
709 // ATTEMPT LOCKING OUTPUT FILE MAX_TRY TIMES BEFORE GIVING UP.
710 while (fcntl(write_fd, F_SETLK, &write_lck) < 0) {
711 if (errno == EAGAIN || errno == EACCES) {
712 // THERE MIGHT BE OTHER ERROR CASES IN WHICH
713 // USERS MIGHT TRY AGAIN
714 if (++tryCtr < MAX_TRY) {
715 jacl_sleep(1000);
716 continue;
717 }
718 sprintf(error_buffer, "File busy unable to get lock on output file.\n");
719 log_error(error_buffer, PLUS_STDOUT);
720 return (exit_function(TRUE));
721 }
722 }
723
724 tryCtr = 0;
725
726 read_fd = fileno(infile);
727 // ATTEMPT LOCKING OUTPUT FILE MAX_TRY TIMES BEFORE GIVING UP.
728 while (fcntl(read_fd, F_SETLK, &read_lck) < 0) {
729 if (errno == EAGAIN || errno == EACCES) {
730 // THERE MIGHT BE OTHER ERROR CASES IN WHICH
731 // USERS MIGHT TRY AGAIN
732 if (++tryCtr < MAX_TRY) {
733 jacl_sleep(1000);
734 continue;
735 }
736 sprintf(error_buffer, "File busy unable to get lock on input file.\n");
737 log_error(error_buffer, PLUS_STDOUT);
738 return (exit_function(TRUE));
739 }
740 }
741 #endif
742 if (word[1] == NULL) {
743 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
744 noproprun();
745 return (exit_function(TRUE));
746 } else {
747 // IF THERE IS ANOTHER RECORD TO READ FROM THE CSV FILE THEN
748 // SET THE field[] CONSTANTS AND INCREMENT THE executionLevel
749 infile->read(csv_buffer, 1024);
750 if (infile->pos() < infile->size()) {
751 i = strlen(csv_buffer);
752 if (csv_parse(&parser_csv, csv_buffer, i, cb1, cb2, (int *) &field_no) != (uint)i) {
753 sprintf(error_buffer, "Error parsing file: %s\n", csv_strerror(csv_error(&parser_csv)));
754 log_error(error_buffer, PLUS_STDOUT);
755 read_lck.l_type = F_UNLCK; // SETTING A READ LOCK
756 fcntl(read_fd, F_SETLK, &read_lck);
757 delete infile;
758 infile = NULL;
759 } else {
760 // A LINE HAS BEEN SUCCESSFULLY READ, EXECUTE THE CONTENTS OF THE LOOP
761 executionLevel++;
762 }
763 } else {
764 write_lck.l_type = F_UNLCK; // REMOVE THE WRITE LOCK
765 fcntl(write_fd, F_SETLK, &write_lck);
766 delete outfile;
767
768 read_lck.l_type = F_UNLCK; // REMOVE THE READ LOCK
769 fcntl(read_fd, F_SETLK, &read_lck);
770 delete infile;
771
772 rename(out_name, in_name);
773
774 outfile = NULL;
775 infile = NULL;
776 }
777 }
778 }
779 }
780 } else if (!strcmp(word[0], "while")) {
781 currentLevel++;
782 /* THIS LOOP COMES BACK TO THE START OF THE LINE CURRENTLY
783 EXECUTING, NOT THE LINE AFTER */
784 top_of_while = before_command;
785 if (word[3] == NULL) {
786 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
787 noproprun();
788 return (exit_function(TRUE));
789 } else if (condition()) {
790 executionLevel++;
791 }
792 } else if (!strcmp(word[0], "whileall")) {
793 currentLevel++;
794 /* THIS LOOP COMES BACK TO THE START OF THE LINE CURRENTLY
795 EXECUTING, NOT THE LINE AFTER */
796 top_of_while = before_command;
797 if (word[3] == NULL) {
798 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
799 noproprun();
800 return (exit_function(TRUE));
801 } else if (and_condition()) {
802 executionLevel++;
803 }
804 } else if (!strcmp(word[0], "loop")) {
805 /* THE LOOP COMMAND LOOPS ONCE FOR EACH DEFINED
806 * OBJECT (FOREACH) */
807 #ifdef GLK
808 top_of_loop = g_vm->glk_stream_get_position(game_stream);
809 #else
810 top_of_loop = ftell(file);
811 #endif
812 if (word[1] == NULL) {
813 // IF NONE IS SUPPLIED DEFAULT TO noun3
814 loop_integer = &noun[2];
815 } else {
816 // STORE THE CONTAINER TO PUT THE CURRENT OBJECT IN
817 loop_integer = container_resolve(word[1]);
818
819 // IF THE SUPPLIED CONTAINER CAN'T BE RESOLVED
820 // DEFAULT TO noun3
821 if (loop_integer == NULL)
822 loop_integer = &noun[2];
823 }
824
825 // SET THE VALUE OF THE LOOP INDEX TO POINT TO THE FIRST OBJECT
826 *loop_integer = 1;
827
828 } else if (!strcmp(word[0], "endloop")) {
829 if (top_of_loop == FALSE) {
830 sprintf(error_buffer, NO_LOOP, executing_function->name);
831 log_error(error_buffer, PLUS_STDOUT);
832 } else {
833 *loop_integer += 1;
834 if (*loop_integer > objects) {
835 top_of_loop = FALSE;
836 *loop_integer = 0;
837 } else {
838 #ifdef GLK
839 g_vm->glk_stream_set_position(game_stream, top_of_loop, seekmode_Start);
840 #else
841 fseek(file, top_of_loop, SEEK_SET);
842 #endif
843 }
844 }
845 } else if (!strcmp(word[0], "select")) {
846 /* THE SELECT COMMAND LOOPS ONCE FOR EACH DEFINED
847 * OBJECT THAT MATCHES THE SUPPLIED CRITERION */
848 #ifdef GLK
849 top_of_select = g_vm->glk_stream_get_position(game_stream);
850 #else
851 top_of_select = ftell(file);
852 #endif
853 if (word[1] == NULL) {
854 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
855 noproprun();
856 return (exit_function(TRUE));
857 } else if (word[2] == NULL) {
858 // IF NONE IS SUPPLIED DEFAULT TO noun3
859 select_integer = &noun[2];
860 } else {
861 // STORE THE CONTAINER TO PUT THE CURRENT OBJECT IN
862 select_integer = container_resolve(word[2]);
863
864 // IF THE SUPPLIED CONTAINER CAN'T BE RESOLVED
865 // DEFAULT TO noun3
866 if (select_integer == NULL) {
867 select_integer = &noun[2];
868 }
869 }
870
871 // SET THE VALUE OF THE SELECT INDEX TO ONE BEFORE THE
872 // FIRST OBJECT. THE NEXT FUNCTION AUTOMATICALLY INCREMENTS
873 // THE INDEX BY ONE AT THE START OF THE WHILE LOOP.
874 *select_integer = 0;
875
876 if (word[1][0] == '!') {
877 criterion_negate = TRUE;
878 strcpy(argument_buffer, &word[1][1]);
879 } else {
880 criterion_negate = FALSE;
881 strcpy(argument_buffer, word[1]);
882 }
883
884 // DETERMINE THE CRITERION FOR SELETION
885 if (!strcmp(argument_buffer, "*held")
886 || !strcmp(argument_buffer, "*here")
887 || !strcmp(argument_buffer, "*anywhere")
888 || !strcmp(argument_buffer, "*present")) {
889 criterion_type = CRI_SCOPE;
890 strncpy(scope_criterion, argument_buffer, 20);
891 } else if ((criterion_value = attribute_resolve(argument_buffer))) {
892 criterion_type = CRI_ATTRIBUTE;
893 } else if ((criterion_value = user_attribute_resolve(argument_buffer))) {
894 criterion_type = CRI_USER_ATTRIBUTE;
895 } else {
896 // USE VALUE OF AS A CATCH ALL IF IT IS NOT AN ATTRIBUTE OR SCOPE
897 criterion_value = value_of(argument_buffer);
898
899 if (value_resolved) {
900 criterion_type = CRI_PARENT;
901 } else {
902 // CAN'T RESOLVE CRITERION
903 criterion_type = CRI_NONE;
904 }
905 }
906
907 if (criterion_type != CRI_NONE) {
908 if (select_next() == FALSE) {
909 *select_integer = 0;
910 top_of_select = 0;
911 }
912 } else {
913 *select_integer = 0;
914 }
915
916 if (*select_integer == 0) {
917 // THERE ARE NO MATCHING OBJECTS SO JUMP TO THE endselect
918 #ifdef GLK
919 (void)glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
920 #else
921 fgets(text_buffer, 1024, file);
922 #endif
923
924 if (encrypted) jacl_decrypt(text_buffer);
925
926 while (text_buffer[0] != '}') {
927 encapsulate();
928 if (word[0] != NULL && !strcmp(word[0], "endselect")) {
929 break;
930 }
931 #ifdef GLK
932 (void)glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
933 #else
934 fgets(text_buffer, 1024, file);
935 #endif
936 }
937 }
938 } else if (!strcmp(word[0], "endselect")) {
939 if (top_of_select == FALSE) {
940 sprintf(error_buffer, NO_LOOP, executing_function->name);
941 log_error(error_buffer, PLUS_STDOUT);
942 } else {
943 if (select_next(/* select_integer, criterion_type, criterion_value, scope_criterion */)) {
944 #ifdef GLK
945 g_vm->glk_stream_set_position(game_stream, top_of_select, seekmode_Start);
946 #else
947 fseek(file, top_of_select, SEEK_SET);
948 #endif
949 } else {
950 *select_integer = 0;
951 top_of_select = 0;
952 }
953 }
954 } else if (!strcmp(word[0], "break")) {
955 currentLevel++;
956 executionLevel--;
957 #ifdef GLK
958 } else if (!strcmp(word[0], "cursor")) {
959 if (word[2] == NULL) {
960 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
961 noproprun(0);
962 return (exit_function(TRUE));
963 } else {
964 if (current_window == statuswin) {
965 g_vm->glk_window_move_cursor(statuswin, value_of(word[1], TRUE), value_of(word[2], TRUE));
966 } else {
967 log_error(BAD_CURSOR, PLUS_STDOUT);
968 }
969 }
970 } else if (!strcmp(word[0], "stop")) {
971 int channel;
972
973 if (SOUND_SUPPORTED->value) {
974 /* SET THE CHANNEL TO STOP, IF SUPPLIED */
975 if (word[1] == NULL) {
976 channel = 0;
977 } else {
978 channel = value_of(word[1], TRUE);
979
980 /* SANITY CHECK THE CHANNEL SELECTED */
981 if (channel < 0 || channel > 7) {
982 channel = 0;
983 }
984 }
985 g_vm->glk_schannel_stop(sound_channel[channel]);
986 }
987 } else if (!strcmp(word[0], "volume")) {
988 int channel, volume;
989
990 if (SOUND_SUPPORTED->value) {
991 /* SET THE CHANNEL TO STOP, IF SUPPLIED */
992 if (word[2] == NULL) {
993 channel = 0;
994 } else {
995 channel = value_of(word[2], TRUE);
996
997 /* SANITY CHECK THE CHANNEL SELECTED */
998 if (channel < 0 || channel > 7) {
999 channel = 0;
1000 }
1001 }
1002
1003 if (word[1] == NULL) {
1004 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1005 noproprun();
1006 return (exit_function(TRUE));
1007 } else {
1008 volume = value_of(word[1], TRUE);
1009
1010 /* SANITY CHECK THE CHANNEL SELECTED */
1011 if (volume < 0) {
1012 volume = 0;
1013 }
1014
1015 if (volume > 100) {
1016 volume = 100;
1017 }
1018
1019 /* STORE A COPY OF THE CURRENT VOLUME FOR ACCESS
1020 * FROM JACL CODE */
1021 sprintf(temp_buffer, "volume[%d]", channel);
1022 cinteger_resolve(temp_buffer)->value = volume;
1023
1024 /* NOW SCALE THE 0-100 VOLUME TO THE 0-65536 EXPECTED
1025 * BY Glk */
1026 volume = volume * 655;
1027
1028 /* SET THE VOLUME */
1029 g_vm->glk_schannel_set_volume(sound_channel[channel], (glui32) volume);
1030 }
1031 }
1032 } else if (!strcmp(word[0], "timer")) {
1033 if (TIMER_SUPPORTED->value && TIMER_ENABLED->value) {
1034 if (word[1] == NULL) {
1035 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1036 noproprun();
1037 return (exit_function(TRUE));
1038 } else {
1039 index = value_of(word[1], TRUE);
1040 /* DON'T ALLOW NEGATIVE VALUES, BUT NO UPPER LIMIT */
1041 if (index < 0) index = 0;
1042
1043 /* SET THE GLK TIMER */
1044 g_vm->glk_request_timer_events((glui32) index);
1045
1046 /* EXPOSE THE CURRENT VALUE THROUGH A JACL CONSTANT
1047 SO THAT GAME CODE CAN READ THE IT */
1048 cinteger_resolve("timer")->value = index;
1049 }
1050 }
1051 } else if (!strcmp(word[0], "sound")) {
1052 int channel;
1053 glui32 repeats;
1054
1055 if (SOUND_SUPPORTED->value && SOUND_ENABLED->value) {
1056 /* SET THE CHANNEL TO USE, IF SUPPLIED */
1057 if (word[2] == NULL) {
1058 channel = 0;
1059 } else {
1060 channel = value_of(word[2], TRUE);
1061
1062 /* SANITY CHECK THE CHANNEL SELECTED */
1063 if (channel < 0 || channel > 7) {
1064 channel = 0;
1065 }
1066 }
1067
1068 /* SET THE NUMBER OF REPEATS, IF SUPPLIED */
1069 if (word[3] == NULL) {
1070 repeats = 1;
1071 } else {
1072 repeats = value_of(word[3], TRUE);
1073 }
1074
1075 if (word[1] == NULL) {
1076 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1077 noproprun();
1078 return (exit_function(TRUE));
1079 } else {
1080 if (g_vm->glk_schannel_play_ext(sound_channel[channel], (glui32) value_of(word[1], TRUE), repeats, channel + 1) == 0) {
1081 /* THE CHANNEL NUMBER IS PASSED SO THAT THE SOUND
1082 * NOTIFICATION EVENT CAN USE THE INFORMATION
1083 * IT HAS 1 ADDED TO IT SO THAT IT IS A NON-ZERO
1084 * NUMBER AND THE EVENT IS ACTIVATED */
1085 sprintf(error_buffer, "Unable to play sound: %ld", value_of(word[1], FALSE));
1086 log_error(error_buffer, PLUS_STDERR);
1087 }
1088 }
1089 }
1090 } else if (!strcmp(word[0], "image")) {
1091 if (GRAPHICS_SUPPORTED->value && GRAPHICS_ENABLED->value) {
1092 if (word[1] == NULL) {
1093 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1094 noproprun();
1095 return (exit_function(TRUE));
1096 } else {
1097 if (!g_vm->loadingSavegame() && g_vm->glk_image_draw(mainwin, (glui32) value_of(word[1], TRUE), imagealign_InlineDown, 0) == 0) {
1098 sprintf(error_buffer, "Unable to draw image: %ld", value_of(word[1], FALSE));
1099 log_error(error_buffer, PLUS_STDERR);
1100 }
1101 }
1102 }
1103 } else if (!strcmp(word[0], "askstring") || !strcmp(word[0], "getstring")) {
1104 if (word[1] == NULL) {
1105 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1106 noproprun(0);
1107 return (exit_function(TRUE));
1108 } else {
1109 /* GET A POINTER TO THE STRING BEING MODIFIED */
1110 if ((resolved_string = string_resolve(word[1])) == NULL) {
1111 unkstrrun(word[1]);
1112 return (exit_function(TRUE));
1113 }
1114
1115 // PROMPT THE USER TO INPUT A STRING AND STORE IT IN THE
1116 // RESOLVED VARIABLE
1117 get_string(resolved_string->value);
1118 }
1119
1120 } else if (!strcmp(word[0], "asknumber") || !strcmp(word[0], "getnumber")) {
1121 int low, high;
1122
1123 int insist = FALSE;
1124
1125 /* THE ONLY DIFFERENCE WITH THE getnumber COMMAND IS THAT
1126 * IT INSISTS THE PLAYER GIVES A LEGAL RESPONSE */
1127 if (!strcmp(word[0], "getnumber")) {
1128 insist = TRUE;
1129 }
1130
1131 if (word[3] != NULL) {
1132 ask_integer = container_resolve(word[1]);
1133 if (ask_integer == NULL) {
1134 unkvarrun(word[1]);
1135 return (exit_function(TRUE));
1136 }
1137
1138 low = value_of(word[2], TRUE);
1139 high = value_of(word[3], TRUE);
1140
1141 if (high == -1 || low == -1) {
1142 return (exit_function(TRUE));
1143 }
1144
1145 *ask_integer = get_number(insist, low, high);
1146 } else {
1147 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1148 noproprun();
1149 return (exit_function(TRUE));
1150 }
1151 } else if (!strcmp(word[0], "getyesorno")) {
1152 if (word[1] != NULL) {
1153 ask_integer = container_resolve(word[1]);
1154 if (ask_integer == NULL) {
1155 unkvarrun(word[1]);
1156 return (exit_function(TRUE));
1157 }
1158
1159 *ask_integer = get_yes_or_no();
1160 } else {
1161 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1162 noproprun();
1163 return (exit_function(TRUE));
1164 }
1165 } else if (!strcmp(word[0], "clear")) {
1166 if (!walkthru_running) {
1167 g_vm->glk_window_clear(current_window);
1168 }
1169 } else if (!strcmp(word[0], "terminate")) {
1170 terminate(0);
1171 return 0;
1172 } else if (!strcmp(word[0], "more")) {
1173 if (word[1] == NULL) {
1174 more("[MORE]");
1175 } else {
1176 more(word[1]);
1177 }
1178 } else if (!strcmp(word[0], "style")) {
1179 /* THIS COMMAND IS USED TO OUTPUT ANSI CODES OR SET GLK
1180 * STREAM STYLES */
1181 if (word[1] == NULL) {
1182 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1183 noproprun();
1184 return (exit_function(TRUE));
1185 } else {
1186 if (!strcmp(word[1], "bold")
1187 || !strcmp(word[1], "emphasised")) {
1188 g_vm->glk_set_style(style_Emphasized);
1189 } else if (!strcmp(word[1], "note")) {
1190 g_vm->glk_set_style(style_Note);
1191 } else if (!strcmp(word[1], "input")) {
1192 g_vm->glk_set_style(style_Input);
1193 } else if (!strcmp(word[1], "header")) {
1194 g_vm->glk_set_style(style_Header);
1195 } else if (!strcmp(word[1], "subheader")) {
1196 g_vm->glk_set_style(style_Subheader);
1197 } else if (!strcmp(word[1], "reverse")
1198 || !strcmp(word[1], "inverse")) {
1199 if (current_window == mainwin) {
1200 g_vm->glk_set_style(style_User2);
1201 } else {
1202 g_vm->glk_set_style(style_User1);
1203 }
1204 } else if (!strcmp(word[1], "pre")
1205 || !strcmp(word[1], "preformatted")) {
1206 g_vm->glk_set_style(style_Preformatted);
1207 } else if (!strcmp(word[1], "normal")) {
1208 g_vm->glk_set_style(style_Normal);
1209 }
1210 }
1211 } else if (!strcmp(word[0], "flush")) {
1212 } else if (!strcmp(word[0], "hyperlink")) {
1213 /* OUTPUT LINK TEXT AS PLAIN TEXT UNDER Glk */
1214 if (word[2] == NULL) {
1215 noproprun();
1216 pop_stack();
1217 return (TRUE);
1218 } else {
1219 write_text(text_of_word(1));
1220 }
1221 #else
1222 #ifdef __NDS__
1223 } else if (!strcmp(word[0], "flush")) {
1224 jflush();
1225 } else if (!strcmp(word[0], "cursor")) {
1226 if (word[2] == NULL) {
1227 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1228 noproprun(0);
1229 return (exit_function(TRUE));
1230 } else {
1231 printf("\x1b[%d;%dH", (int) value_of(word[1], TRUE), (int) value_of(word[2], TRUE));
1232 }
1233 } else if (!strcmp(word[0], "stop")) {
1234 } else if (!strcmp(word[0], "volume")) {
1235 } else if (!strcmp(word[0], "timer")) {
1236 } else if (!strcmp(word[0], "sound")) {
1237 } else if (!strcmp(word[0], "image")) {
1238 } else if (!strcmp(word[0], "askstring") || !strcmp(word[0], "getstring")) {
1239 if (word[1] == NULL) {
1240 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1241 noproprun(0);
1242 return (exit_function(TRUE));
1243 } else {
1244 /* GET A POINTER TO THE STRING BEING MODIFIED */
1245 if ((resolved_string = string_resolve(word[1])) == NULL) {
1246 unkstrrun(word[1]);
1247 return (exit_function(TRUE));
1248 }
1249
1250 // PROMPT THE USER TO INPUT A STRING AND STORE IT IN THE
1251 // RESOLVED VARIABLE
1252 get_string(resolved_string->value);
1253 }
1254
1255 } else if (!strcmp(word[0], "asknumber") || !strcmp(word[0], "getnumber")) {
1256 int low, high;
1257
1258 int insist = FALSE;
1259
1260 /* THE ONLY DIFFERENCE WITH THE getnumber COMMAND IS THAT
1261 * IT INSISTS THE PLAYER GIVES A LEGAL RESPONSE */
1262 if (!strcmp(word[0], "getnumber")) {
1263 insist = TRUE;
1264 }
1265
1266 if (word[3] != NULL) {
1267 ask_integer = container_resolve(word[1]);
1268 if (ask_integer == NULL) {
1269 unkvarrun(word[1]);
1270 return (exit_function(TRUE));
1271 }
1272
1273 low = value_of(word[2], TRUE);
1274 high = value_of(word[3], TRUE);
1275
1276 if (high == -1 || low == -1) {
1277 return (exit_function(TRUE));
1278 }
1279
1280 *ask_integer = get_number(insist, low, high);
1281 } else {
1282 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1283 noproprun();
1284 return (exit_function(TRUE));
1285 }
1286 } else if (!strcmp(word[0], "getyesorno")) {
1287 if (word[1] != NULL) {
1288 ask_integer = container_resolve(word[1]);
1289 if (ask_integer == NULL) {
1290 unkvarrun(word[1]);
1291 return (exit_function(TRUE));
1292 }
1293
1294 *ask_integer = get_yes_or_no();
1295 } else {
1296 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1297 noproprun();
1298 return (exit_function(TRUE));
1299 }
1300 } else if (!strcmp(word[0], "clear")) {
1301 clrscrn();
1302 } else if (!strcmp(word[0], "terminate")) {
1303 terminate(0);
1304 return;
1305 } else if (!strcmp(word[0], "more")) {
1306 if (word[1] == NULL) {
1307 more("[MORE]");
1308 } else {
1309 more(word[1]);
1310 }
1311 } else if (!strcmp(word[0], "style")) {
1312 /* THIS COMMAND IS USED TO OUTPUT ANSI CODES OR SET GLK
1313 * STREAM STYLES */
1314 if (word[1] == NULL) {
1315 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1316 noproprun();
1317 return (exit_function(TRUE));
1318 } else {
1319 if (!strcmp(word[1], "bold")
1320 || !strcmp(word[1], "emphasised")) {
1321 printf("\x1b[37;1m"); // SET TO BRIGHT WHITE
1322 bold_mode = TRUE;
1323 } else if (!strcmp(word[1], "note")) {
1324 printf("\x1b[34;1m"); // SET TO BRIGHT BLUE
1325 note_mode = TRUE;
1326 } else if (!strcmp(word[1], "input")) {
1327 printf("\x1b[32;0m"); // SET TO DIM GREEN
1328 input_mode = TRUE;
1329 } else if (!strcmp(word[1], "header")) {
1330 printf("\x1b[37;0m"); // SET TO DIM WHITE
1331 } else if (!strcmp(word[1], "subheader")) {
1332 printf("\x1b[33;1m"); // SET TO BRIGHT YELLOW
1333 subheader_mode = TRUE;
1334 } else if (!strcmp(word[1], "reverse")
1335 || !strcmp(word[1], "inverse")) {
1336 printf("\x1b[7m"); // SET TO DIM WHITE
1337 reverse_mode = TRUE;
1338 } else if (!strcmp(word[1], "pre")
1339 || !strcmp(word[1], "preformatted")) {
1340 printf("\x1b[37;0m"); // SET TO DIM WHITE
1341 pre_mode = TRUE;
1342 } else if (!strcmp(word[1], "normal")) {
1343 printf("\x1b[37;0m"); // SET TO DIM WHITE
1344 bold_mode = FALSE;
1345 pre_mode = FALSE;
1346 reverse_mode = FALSE;
1347 input_mode = FALSE;
1348 subheader_mode = FALSE;
1349 note_mode = FALSE;
1350 }
1351 }
1352 } else if (!strcmp(word[0], "hyperlink")) {
1353 /* OUTPUT LINK TEXT AS PLAIN TEXT UNDER Glk */
1354 if (word[2] == NULL) {
1355 noproprun();
1356 pop_stack();
1357 return (TRUE);
1358 } else {
1359 write_text(text_of_word(1));
1360 }
1361 #else
1362 /* HERE STARTS THE CGIJACL-ONLY FUNCTIONS */
1363 } else if (!strcmp(word[0], "option")) {
1364 /* USED TO ADD AN OPTION TO AN HTML LIST */
1365 if (word[1] == NULL) {
1366 noproprun();
1367 pop_stack();
1368 return (TRUE);
1369 } else {
1370 index = value_of(word[1]);
1371 if (word[2] != NULL) {
1372 sprintf(option_buffer, "<option value=\"%d\">",
1373 index);
1374 } else {
1375 object_names(index, temp_buffer);
1376 sprintf(option_buffer, "<option value=\"%s\">", temp_buffer);
1377 }
1378
1379 write_text(option_buffer);
1380 list_output(index, TRUE);
1381 write_text(temp_buffer);
1382
1383 }
1384 } else if (!strcmp(word[0], "getenv")) {
1385 struct string_type *resolved_setstring = NULL;
1386
1387 if (word[2] == NULL) {
1388 noproprun();
1389 pop_stack();
1390 return (TRUE);
1391 } else {
1392 // GET A POINTER TO THE STRING BEING MODIFIED
1393 if ((resolved_setstring = string_resolve(word[1])) == NULL) {
1394 unkstrrun(word[1]);
1395 return (exit_function(TRUE));
1396 }
1397
1398 // COPY THE VARIABLE OF THE CGI VARIABLE INTO THE SPECIFIED STRING VARIABLE
1399 if (getenv(text_of_word(2)) != NULL) {
1400 strncpy(resolved_setstring->value, getenv(text_of_word(2)), 255);
1401 } else {
1402 strncpy(resolved_setstring->value, "", 255);
1403 }
1404 }
1405 } else if (!strcmp(word[0], "button")) {
1406 /* USED TO CREATE AN HTML BUTTON */
1407 if (word[1] == NULL) {
1408 noproprun();
1409 pop_stack();
1410 return (TRUE);
1411 }
1412 if (word[2] != NULL) {
1413 sprintf(option_buffer, "<input class=~button~ type=~image~ src=~%s~ name=~verb~ value=~", text_of_word(2));
1414 strcat(option_buffer, text_of_word(1));
1415 strcat(option_buffer, "~>");
1416 write_text(option_buffer);
1417 } else {
1418 sprintf(option_buffer, "<input class=~button~ type=~submit~ style=~width: 90px; margin: 5px;~ name=~verb~ value=~%s~>", text_of_word(1));
1419 write_text(option_buffer);
1420 }
1421 } else if (!strcmp(word[0], "hidden")) {
1422 sprintf(temp_buffer, "<INPUT TYPE=\"hidden\" NAME=\"user_id\" VALUE=\"%s\">", user_id);
1423 write_text(temp_buffer);
1424 } else if (!strcmp(word[0], "control")) {
1425 /* USED TO CREATE A HYPERLINK THAT IS AN IMAGE */
1426 if (word[2] == NULL) {
1427 noproprun();
1428 pop_stack();
1429 return (TRUE);
1430 } else {
1431 sprintf(option_buffer, "<a href=\"?command=%s&user_id=%s\"><img border=0 SRC=\"", text_of_word(2), user_id);
1432 strcat(option_buffer, text_of_word(1));
1433 strcat(option_buffer, "\"></a>");
1434 write_text(option_buffer);
1435 }
1436 } else if (!strcmp(word[0], "hyperlink") || !strcmp(word[0], "hyperlinkNE")) {
1437 string_buffer[0] = 0;
1438
1439 /* USED TO CREATE A HYPERLINK WITH SESSION INFORMATION INCLUDED */
1440 if (word[2] == NULL) {
1441 noproprun();
1442 pop_stack();
1443 return (TRUE);
1444 } else {
1445 char *encoded;
1446
1447 if (!strcmp(word[0], "hyperlink")) {
1448 encoded = url_encode(text_of_word(2));
1449 } else {
1450 encoded = text_of_word(2);
1451 }
1452
1453 if (word[3] == NULL) {
1454 sprintf(string_buffer, "<a href=\"?command=%s&user_id=%s\">", encoded, user_id);
1455 strcat(string_buffer, text_of_word(1));
1456 strcat(string_buffer, "</a>");
1457 } else {
1458 sprintf(string_buffer, "<a class=\"%s\" href=\"?command=", text_of_word(3));
1459 strcat(string_buffer, encoded);
1460 sprintf(option_buffer, "&user_id=%s\">%s</a>", user_id, text_of_word(1));
1461 strcat(string_buffer, option_buffer);
1462 }
1463
1464 if (!strcmp(word[0], "hyperlink")) {
1465 free(encoded);
1466 }
1467
1468 write_text(string_buffer);
1469 }
1470 } else if (!strcmp(word[0], "prompt")) {
1471 /* USED TO OUTPUT A HTML INPUT CONTROL THAT CONTAINS SESSION INFORMATION */
1472 if (word[1] != NULL) {
1473 sprintf(temp_buffer, "<input id=\"JACLCommandPrompt\" type=text name=~command~ onKeyPress=~%s~>\n", word[1]);
1474 write_text(temp_buffer);
1475 } else {
1476 sprintf(temp_buffer, "<input id=\"JACLCommandPrompt\" type=text name=~command~>\n");
1477 write_text(temp_buffer);
1478 }
1479 sprintf(temp_buffer, "<input type=hidden name=\"user_id\" value=\"%s\">", user_id);
1480 write_text(temp_buffer);
1481 } else if (!strcmp(word[0], "style")) {
1482 /* THIS COMMAND IS USED TO OUTPUT ANSI CODES OR SET GLK
1483 * STREAM STYLES */
1484 if (word[1] == NULL) {
1485 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1486 noproprun();
1487 return (exit_function(TRUE));
1488 } else {
1489 if (!strcmp(word[1], "bold")
1490 || !strcmp(word[1], "emphasised")) {
1491 write_text("<b>");
1492 style_stack[style_index++] = BOLD;
1493 } else if (!strcmp(word[1], "note")) {
1494 write_text("<i>");
1495 style_stack[style_index++] = NOTE;
1496 } else if (!strcmp(word[1], "input")) {
1497 write_text("<i>");
1498 style_stack[style_index++] = INPUT;
1499 } else if (!strcmp(word[1], "header")) {
1500 write_text("<h1>");
1501 style_stack[style_index++] = HEADER;
1502 } else if (!strcmp(word[1], "subheader")) {
1503 write_text("<h2>");
1504 style_stack[style_index++] = SUBHEADER;
1505 } else if (!strcmp(word[1], "reverse")
1506 || !strcmp(word[1], "inverse")) {
1507 write_text("<b>");
1508 style_stack[style_index++] = REVERSE;
1509 } else if (!strcmp(word[1], "pre")
1510 || !strcmp(word[1], "preformatted")) {
1511 write_text("<pre>");
1512 style_stack[style_index++] = PRE;
1513 } else if (!strcmp(word[1], "normal")) {
1514 style_index--;
1515 for (; style_index > -1; style_index--) {
1516 switch (style_stack[style_index]) {
1517 case BOLD:
1518 write_text("</b>");
1519 break;
1520 case NOTE:
1521 write_text("</i>");
1522 break;
1523 case INPUT:
1524 write_text("</i>");
1525 break;
1526 case HEADER:
1527 write_text("</h1>");
1528 break;
1529 case SUBHEADER:
1530 write_text("</h2>");
1531 break;
1532 case REVERSE:
1533 write_text("</b>");
1534 break;
1535 case PRE:
1536 write_text("</pre>");
1537 break;
1538 }
1539 }
1540 style_index = 0;
1541 }
1542 }
1543 /* THESE FINAL COMMANDS HAVE NO EFFECT UNDER CGIJACL
1544 AND THERE IS NO HARM IN IGNORING THEM */
1545 } else if (!strcmp(word[0], "flush")) {
1546 } else if (!strcmp(word[0], "image")) {
1547 if (word[1] == NULL) {
1548 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1549 noproprun(0);
1550 return (exit_function(TRUE));
1551 } else {
1552 if (word[2] == NULL) {
1553 sprintf(option_buffer, "<img src=~%s~>", text_of_word(1));
1554 } else {
1555 sprintf(option_buffer, "<img class=~%s~ src=~%s~>", text_of_word(2), text_of_word(1));
1556 }
1557
1558 write_text(option_buffer);
1559 }
1560 } else if (!strcmp(word[0], "sound")) {
1561 if (word[2] == NULL) {
1562 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1563 noproprun(0);
1564 return (exit_function(TRUE));
1565 } else {
1566 write_text("<audio autoplay=~autoplay~>");
1567 if (word[3] == NULL) {
1568 sprintf(option_buffer, "<source src=~%s~ type=~%s~>", text_of_word(1), text_of_word(2));
1569 write_text(option_buffer);
1570 }
1571 write_text("</audio>");
1572 }
1573 } else if (!strcmp(word[0], "cursor")) {
1574 } else if (!strcmp(word[0], "timer")) {
1575 } else if (!strcmp(word[0], "volume")) {
1576 } else if (!strcmp(word[0], "askstring") || !strcmp(word[0], "getstring")) {
1577 } else if (!strcmp(word[0], "asknumber") || !strcmp(word[0], "getnumber")) {
1578 } else if (!strcmp(word[0], "getyesorno")) {
1579 } else if (!strcmp(word[0], "clear")) {
1580 } else if (!strcmp(word[0], "more")) {
1581 } else if (!strcmp(word[0], "terminate")) {
1582 #endif
1583 #endif
1584 } else if (!strcmp(word[0], "proxy")) {
1585 /* THE PROXY COMMAND ISSUES A MOVE ON THE PLAYER'S BEHALF
1586 * ALL STATE MUST BE SAVED SO THE CURRENT MOVE CAN CONTINUE
1587 * ONCE THE PROXIED MOVE IS COMPLETE */
1588 #ifdef GLK
1589 push_stack(g_vm->glk_stream_get_position(game_stream));
1590 #else
1591 push_stack(ftell(file));
1592 #endif
1593 push_proxy();
1594
1595 build_proxy();
1596
1597 // TEXT BUFFER IS THE NORMAL ARRAY FOR HOLDING THE PLAYERS
1598 // MOVE FOR PROCESSING
1599 strncpy(text_buffer, proxy_buffer, 1024);
1600
1601 command_encapsulate();
1602
1603 jacl_truncate();
1604
1605 preparse();
1606
1607 pop_proxy();
1608
1609 pop_stack();
1610 } else if (!strcmp(word[0], "override")) {
1611 /* TELLS THE INTERPRETER TO LOOK FOR AN _override FUNCTION
1612 * TO EXECUTE IN PLACE OF ANY CODE THAT FOLLOWS THIS LINE.
1613 * THIS COMMAND IS USED EXCLUSIVELY IN GLOBAL FUNCTIONS
1614 * ASSOCIATED WITH GRAMMAR LINES */
1615 if (execute(override_) == TRUE) {
1616 return (exit_function(TRUE));
1617 } else {
1618 if (execute(default_function) == TRUE) {
1619 return (exit_function(TRUE));
1620 }
1621 }
1622 } else if (!strcmp(word[0], "execute") || !strcmp(word[0], "call")) {
1623 /* CALLS ANOTHER JACL FUNCTION */
1624 if (word[1] == NULL) {
1625 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1626 noproprun();
1627 return (exit_function(TRUE));
1628 } else {
1629 /* RESOLVE ALL THE TEXT AND STORE IT IN A TEMPORARY BUFFER*/
1630 string_buffer[0] = 0;
1631
1632 for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
1633 strcat(string_buffer, arg_text_of_word(counter));
1634 }
1635
1636 if (function_resolve(string_buffer) == NULL && !strcmp(word[0], "execute")) {
1637 char *argstart;
1638
1639 /* REMOVE ANY PARAMETERS FROM FUNCTION NAME
1640 BEFORE DISPLAYING ERROR MESSAGE */
1641 argstart = strchr(string_buffer, '<');
1642 if (argstart != NULL)
1643 *argstart = 0;
1644
1645 sprintf(error_buffer, UNDEFINED_FUNCTION, executing_function->name, string_buffer);
1646 log_error(error_buffer, PLUS_STDOUT);
1647 } else {
1648 execute(string_buffer);
1649 }
1650 }
1651 } else if (!strcmp(word[0], "points")) {
1652 /* INCREASE THE PLAYER'S SCORE AND POTENTIALLY INFORM THEM OF THE INCREASE */
1653 if (word[1] != NULL) {
1654 SCORE->value += value_of(word[1], TRUE);
1655 if (NOTIFY->value) {
1656 #ifdef GLK
1657 g_vm->glk_set_style(style_Note);
1658 #else
1659 #ifdef __NDS__
1660 printf("\x1b[34;1m"); // SET TO BRIGHT BLUE
1661 note_mode = TRUE;
1662 #else
1663 write_text("<b><i>");
1664 #endif
1665 #endif
1666 write_text(cstring_resolve("SCORE_UP")->value);
1667 sprintf(temp_buffer, "%ld", value_of(word[1], TRUE));
1668 write_text(temp_buffer);
1669 if (value_of(word[1], TRUE) == 1) {
1670 write_text(cstring_resolve("POINT")->value);
1671 } else {
1672 write_text(cstring_resolve("POINTS")->value);
1673 }
1674 #ifdef GLK
1675 g_vm->glk_set_style(style_Normal);
1676 #else
1677 #ifdef __NDS__
1678 printf("\x1b[37;0m"); // SET TO DIM WHITE
1679 note_mode = FALSE;
1680 #else
1681 write_text("</i></b>");
1682 #endif
1683 #endif
1684 }
1685 } else {
1686 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1687 noproprun();
1688 return (exit_function(TRUE));
1689 }
1690 } else if (!strcmp(word[0], "print")) {
1691 int non_space = FALSE;
1692
1693 // DISPLAYS A BLOCK OF PLAIN TEXT UNTIL IT FINDS A
1694 // LINE THAT STARTS WITH A '.' OR A '}'
1695 #ifdef GLK
1696 (void)glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
1697 #else
1698 fgets(text_buffer, 1024, file);
1699 #endif
1700
1701 if (encrypted) jacl_decrypt(text_buffer);
1702
1703 while (text_buffer[0] != '.' && text_buffer[0] != '}') {
1704 index = 0;
1705 non_space = FALSE;
1706
1707 /* REMOVE ANY NEWLINE CHARACTERS */
1708 while (text_buffer[index] != 0) {
1709 if (text_buffer[index] == '|' && non_space == FALSE) {
1710 /* THE BAR CHARACTER IS CHANGED TO A SPACE TO
1711 * ALLOW INDENTING OF NEW PARAGRAPHS ETC */
1712 text_buffer[index] = ' ';
1713 } else if (text_buffer[index] == '\r') {
1714 text_buffer[index] = 0;
1715 break;
1716 } else if (text_buffer[index] == '\n') {
1717 text_buffer[index] = 0;
1718 break;
1719 } else if (text_buffer[index] != ' ' && text_buffer[index] != '\t') {
1720 non_space = TRUE;
1721 }
1722
1723 index++;
1724 }
1725
1726 if (text_buffer[0] != 0) {
1727 // CHECK IF THERE IS THE NEED TO ADD AN
1728 // IMPLICIT SPACE
1729 index = strlen(text_buffer);
1730
1731 if (text_buffer[index - 1] == '\\') {
1732 // A BACKSLASH IS USED TO INDICATE AN IMPLICIT
1733 // SPACE SHOULD NOT BE PRINTED
1734 text_buffer[index - 1] = 0;
1735 } else if (text_buffer[index - 1] != '^') {
1736 // ADD AN IMPLICIT SPACE IF THE PREVIOUS LINE
1737 // DIDN'T END WITH A CARRIAGE RETURN
1738 strcat(text_buffer, " ");
1739 }
1740
1741 // OUTPUT THE LINE READ AS PLAIN TEXT
1742 write_text(text_buffer);
1743 }
1744
1745 // GET THE NEXT LINE
1746 #ifdef GLK
1747 (void)glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
1748 #else
1749 fgets(text_buffer, 1024, file);
1750 #endif
1751
1752 if (encrypted) jacl_decrypt(text_buffer);
1753 }
1754 } else if (!strcmp(word[0], "mesg")) {
1755 for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
1756 warning("%s", text_of_word(counter));
1757 }
1758 } else if (!strcmp(word[0], "error")) {
1759 write_text("ERROR: In function ~");
1760 write_text(executing_function->name);
1761 write_text("~, ");
1762 for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
1763 write_text(text_of_word(counter));
1764 }
1765 } else if (!strcmp(word[0], "debug") && DEBUG->value) {
1766 write_text("DEBUG: ");
1767 for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
1768 write_text(text_of_word(counter));
1769 }
1770 } else if (!strcmp(word[0], "write")) {
1771 for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
1772 output = text_of_word(counter);
1773 if (*output != 0) {
1774 // IF THE OUTPUT ISN'T AN EMPTY STRING, DISPLAY IT
1775 write_text(output);
1776 }
1777 }
1778 } else if (!strcmp(word[0], "length")) {
1779 if (word[2] == NULL) {
1780 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1781 noproprun(0);
1782 return (exit_function(TRUE));
1783 } else {
1784 if ((container = container_resolve(word[1])) == NULL) {
1785 unkvarrun(word[1]);
1786 return (exit_function(TRUE));
1787 }
1788
1789 *container = strlen(text_of(word[2]));
1790 }
1791 } else if (!strcmp(word[0], "savegame")) {
1792 if (word[1] == NULL) {
1793 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1794 noproprun();
1795 return (exit_function(TRUE));
1796 } else {
1797 if ((container = container_resolve(word[1])) == NULL) {
1798 unkvarrun(word[1]);
1799 return (exit_function(TRUE));
1800 } else {
1801 *container = save_interaction();
1802 }
1803 }
1804 } else if (!strcmp(word[0], "restoregame")) {
1805 if (word[1] == NULL) {
1806 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1807 noproprun();
1808 return (exit_function(TRUE));
1809 } else {
1810 if ((container = container_resolve(word[1])) == NULL) {
1811 unkvarrun(word[1]);
1812 return (exit_function(TRUE));
1813 } else {
1814 *container = restore_interaction();
1815 }
1816 }
1817 } else if (!strcmp(word[0], "restartgame")) {
1818 restart_game();
1819 execute("+intro");
1820 eachturn();
1821 #ifdef GLK
1822 } else if (!strcmp(word[0], "undomove")) {
1823 undoing();
1824 } else if (!strcmp(word[0], "updatestatus")) {
1825 status_line();
1826 #else
1827 } else if (!strcmp(word[0], "undomove")) {
1828 } else if (!strcmp(word[0], "updatestatus")) {
1829 #endif
1830 } else if (!strcmp(word[0], "split")) {
1831
1832 // 0 1 2 3 4
1833 // split counter source delimiter destination
1834
1835 int *split_container;
1836 char split_buffer[256] = "";
1837 char container_buffer[256] = "";
1838 char delimiter[256] = "";
1839 char *match = NULL;
1840 struct string_type *resolved_splitstring = NULL;
1841
1842 strcpy(split_buffer, text_of_word(2));
1843 strcpy(delimiter, text_of_word(3));
1844
1845 char *source = split_buffer;
1846
1847 if (word[4] == NULL) {
1848 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1849 noproprun(0);
1850 return (exit_function(TRUE));
1851 } else {
1852 split_container = container_resolve(var_text_of_word(1));
1853
1854 if (split_container == NULL) {
1855 unkvarrun(var_text_of_word(1));
1856 return (exit_function(TRUE));
1857 } else {
1858 *split_container = 0;
1859 match = source; // THERE IS ALWAYS ONE MATCH, EVEN IF
1860 // NO DELIMETERS ARE FOUND
1861
1862 while ((match = strstr(source, delimiter))) {
1863 *match = 0;
1864 strcpy(container_buffer, var_text_of_word(4));
1865 strcat(container_buffer, "[");
1866 sprintf(integer_buffer, "%d", *split_container);
1867 strcat(container_buffer, integer_buffer);
1868 strcat(container_buffer, "]");
1869
1870 if ((resolved_splitstring = string_resolve(container_buffer)) == NULL) {
1871 unkstrrun(var_text_of_word(4));
1872 return (exit_function(TRUE));
1873 } else {
1874 strcpy(resolved_splitstring->value, source);
1875 source = match + strlen(delimiter);
1876 (*split_container)++;
1877 }
1878 }
1879 strcpy(container_buffer, var_text_of_word(4));
1880 strcat(container_buffer, "[");
1881 sprintf(integer_buffer, "%d", *split_container);
1882 strcat(container_buffer, integer_buffer);
1883 strcat(container_buffer, "]");
1884
1885 if ((resolved_splitstring = string_resolve(container_buffer)) == NULL) {
1886 unkstrrun(word[1]);
1887 return (exit_function(TRUE));
1888 } else {
1889 strcpy(resolved_splitstring->value, source);
1890 (*split_container)++;
1891 }
1892 }
1893 }
1894 } else if (!strcmp(word[0], "setstring") ||
1895 !strcmp(word[0], "addstring")) {
1896 char setstring_buffer[2048] = "";
1897 struct string_type *resolved_setstring = NULL;
1898
1899 if (word[2] == NULL) {
1900 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1901 noproprun(0);
1902 return (exit_function(TRUE));
1903 } else {
1904 /* GET A POINTER TO THE STRING BEING MODIFIED */
1905 if ((resolved_setstring = string_resolve(var_text_of_word(1))) == NULL) {
1906 unkstrrun(word[1]);
1907 return (exit_function(TRUE));
1908 }
1909
1910 /* RESOLVE ALL THE TEXT AND STORE IT IN A TEMPORARY BUFFER*/
1911 for (counter = 2; word[counter] != NULL && counter < MAX_WORDS; counter++) {
1912 strcat(setstring_buffer, text_of_word(counter));
1913 }
1914
1915 /* setstring_buffer IS NOW FILLED, COPY THE UP TO 256 BYTES OF
1916 * IT INTO THE STRING */
1917 if (!strcmp(word[0], "setstring")) {
1918 strncpy(resolved_setstring->value, setstring_buffer, 255);
1919 } else {
1920 /* CALCULATE HOW MUCH SPACE IS LEFT IN THE STRING */
1921 counter = 255 - strlen(resolved_setstring->value);
1922 /* THIS IS A addstring COMMAND, SO USE STRNCAT INSTEAD */
1923 strncat(resolved_setstring->value, setstring_buffer, counter);
1924 }
1925 }
1926 } else if (!strcmp(word[0], "padstring")) {
1927 char setstring_buffer[2048] = "";
1928 struct string_type *resolved_setstring = NULL;
1929 string_buffer[0] = 0;
1930
1931 if (word[3] == NULL) {
1932 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1933 noproprun(0);
1934 return (exit_function(TRUE));
1935 } else {
1936 /* GET A POINTER TO THE STRING BEING MODIFIED */
1937 if ((resolved_setstring = string_resolve(word[1])) == NULL) {
1938 unkstrrun(word[1]);
1939 return (exit_function(TRUE));
1940 }
1941
1942 index = value_of(word[3], TRUE);
1943
1944 for (counter = 0; counter < index; counter++) {
1945 strcat(setstring_buffer, text_of_word(2));
1946 }
1947
1948 /* setstring_buffer IS NOW FILLED, COPY THE UP TO 256 BYTES OF
1949 * IT INTO THE STRING */
1950 strncpy(resolved_setstring->value, setstring_buffer, 255);
1951 }
1952 } else if (!strcmp(word[0], "return")) {
1953 /* RETURN FROM THIS FUNCTION, POSSIBLY RETURNING AN INTEGER VALUE */
1954 if (word[1] == NULL) {
1955 return (exit_function(TRUE));
1956 } else {
1957 index = value_of(word[1], TRUE);
1958 return (exit_function(index));
1959 }
1960 } else if (!strcmp(word[0], "position")) {
1961 /* MOVE AN OBJECT TO ITS NEW X,Y COORDINATES BASED ON ITS CURRENT VALUES
1962 * FOR x, y, bearing, velocity */
1963 if (word[1] == NULL) {
1964 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1965 noproprun();
1966 return (exit_function(TRUE));
1967 } else {
1968 object_1 = value_of(word[1], TRUE);
1969
1970 if (object_1 < 1 || object_1 > objects) {
1971 badptrrun(word[1], object_1);
1972 return (exit_function(TRUE));
1973 } else {
1974 new_position((double) object[object_1]->X,
1975 (double) object[object_1]->Y,
1976 (double) object[object_1]->BEARING,
1977 (double) object[object_1]->VELOCITY);
1978
1979 object[object_1]->X = new_x;
1980 object[object_1]->Y = new_y;
1981 }
1982 }
1983 } else if (!strcmp(word[0], "bearing")) {
1984 /* CALCULATE THE BEARING BETWEEN TWO OBJECTS */
1985 if (word[3] == NULL) {
1986 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
1987 noproprun();
1988 return (exit_function(TRUE));
1989 } else {
1990 if ((container = container_resolve(word[1])) == NULL) {
1991 unkvarrun(word[1]);
1992 return (exit_function(TRUE));
1993 }
1994
1995 object_1 = value_of(word[2], TRUE);
1996
1997 if (object_1 < 1 || object_1 > objects) {
1998 badptrrun(word[2], object_1);
1999 return (exit_function(TRUE));
2000 } else {
2001 object_2 = value_of(word[3], TRUE);
2002
2003 if (object_2 < 1 || object_2 > objects) {
2004 badptrrun(word[3], object_2);
2005 return (exit_function(TRUE));
2006 } else {
2007 if (container != NULL
2008 && object_1 != FALSE
2009 && object_2 != FALSE) {
2010 *container = bearing((double) object[object_1]->X,
2011 (double) object[object_1]->Y,
2012 (double) object[object_2]->X,
2013 (double) object[object_2]->Y);
2014 }
2015 }
2016 }
2017 }
2018 } else if (!strcmp(word[0], "distance")) {
2019 /* CALCULATE THE DISTANCE BETWEEN TWO OBJECTS */
2020 if (word[3] == NULL) {
2021 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
2022 noproprun();
2023 return (exit_function(TRUE));
2024 } else {
2025 container = container_resolve(word[1]);
2026
2027 object_1 = value_of(word[2], TRUE);
2028
2029 if (object_1 < 1 || object_1 > objects) {
2030 badptrrun(word[2], object_1);
2031 return (exit_function(TRUE));
2032 } else {
2033 object_2 = value_of(word[3], TRUE);
2034
2035 if (object_2 < 1 || object_2 > objects) {
2036 badptrrun(word[3], object_2);
2037 return (exit_function(TRUE));
2038 } else {
2039 if (container != NULL
2040 && object_1 != FALSE
2041 && object_2 != FALSE) {
2042 *container = distance((double)
2043 object[object_1]->X,
2044 (double)
2045 object[object_1]->Y,
2046 (double)
2047 object[object_2]->X,
2048 (double)
2049 object[object_2]->Y);
2050 }
2051 }
2052 }
2053 }
2054 } else if (!strcmp(word[0], "dir_to") ||
2055 !strcmp(word[0], "npc_to")) {
2056 /* CALCULATE THE FIRST DIRECTION TO TRAVEL IN GET TO
2057 * A SPECIFIED LOCATION */
2058 if (word[3] == NULL) {
2059 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
2060 noproprun();
2061 return (exit_function(TRUE));
2062 } else {
2063 container = container_resolve(word[1]);
2064
2065 object_1 = value_of(word[2], TRUE);
2066
2067 if (object_1 < 1 || object_1 > objects) {
2068 badptrrun(word[2], object_1);
2069 return (exit_function(TRUE));
2070 } else {
2071 object_2 = value_of(word[3], TRUE);
2072
2073 if (object_2 < 1 || object_2 > objects) {
2074 badptrrun(word[3], object_2);
2075 return (exit_function(TRUE));
2076 } else {
2077 if (container != NULL
2078 && object_1 != FALSE
2079 && object_2 != FALSE) {
2080 if (!strcmp(word[0], "dir_to")) {
2081 *container = find_route(object_1, object_2, TRUE);
2082 } else {
2083 *container = find_route(object_1, object_2, FALSE);
2084 }
2085 }
2086 }
2087 }
2088 }
2089 } else if (!strcmp(word[0], "set")) {
2090 /* SET THE VALUE OF AN ELEMENT TO A SUPPLIED INTEGER */
2091 if (word[3] == NULL) {
2092 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
2093 noproprun();
2094 return (exit_function(TRUE));
2095 } else {
2096 container = container_resolve(var_text_of_word(1));
2097
2098 if (container == NULL) {
2099 unkvarrun(word[1]);
2100 return (exit_function(TRUE));
2101 } else {
2102 int mark = 2; // SET mark TO POINT TO THE FIRST OPERATOR
2103 while (word[mark + 1] != NULL) {
2104 counter = value_of(word[mark + 1], TRUE);
2105
2106 if (word[mark][0] == '+')
2107 *container += counter;
2108 else if (word[mark][0] == '-')
2109 *container -= counter;
2110 else if (word[mark][0] == '*')
2111 *container = *container * counter;
2112 else if (word[mark][0] == '%')
2113 *container = *container % counter;
2114 else if (word[mark][0] == '/') {
2115 if (counter == 0) {
2116 sprintf(error_buffer, DIVIDE_BY_ZERO,
2117 executing_function->name);
2118 log_error(error_buffer, PLUS_STDOUT);
2119 } else
2120 *container = *container / counter;
2121 } else if (!strcmp(word[mark], "locationof")) {
2122 *container = grand_of(counter, FALSE);
2123 } else if (!strcmp(word[mark], "grandof")) {
2124 *container = grand_of(counter, TRUE);
2125 } else if (word[mark][0] == '=') {
2126 *container = counter;
2127 } else {
2128 sprintf(error_buffer, ILLEGAL_OPERATOR,
2129 executing_function->name,
2130 word[2]);
2131 log_error(error_buffer, PLUS_STDOUT);
2132 }
2133
2134 mark += 2;
2135 }
2136 }
2137 }
2138 } else if (!strcmp(word[0], "ensure")) {
2139 /* USED TO GIVE OR TAKE AN ATTRIBUTE TO OR FROM AND OBJECT */
2140 if (word[3] == NULL) {
2141 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
2142 noproprun();
2143 return (exit_function(TRUE));
2144 } else {
2145 if ((bit_mask = attribute_resolve(arg_text_of(word[3])))) {
2146 index = value_of(word[1], TRUE);
2147 if (index < 1 || index > objects) {
2148 badptrrun(word[1], index);
2149 return (exit_function(TRUE));
2150 } else {
2151 if (!strcmp(word[2], "has")) {
2152 object[index]->attributes =
2153 object[index]->attributes | bit_mask;
2154 } else if (!strcmp(word[2], "hasnt")) {
2155 bit_mask = ~bit_mask;
2156 object[index]->attributes =
2157 object[index]->attributes & bit_mask;
2158 }
2159 }
2160 } else if ((bit_mask = user_attribute_resolve(arg_text_of(word[3])))) {
2161 index = value_of(word[1], TRUE);
2162 if (index < 1 || index > objects) {
2163 badptrrun(word[1], index);
2164 return (exit_function(TRUE));
2165 } else {
2166 if (!strcmp(word[2], "has")) {
2167 object[index]->user_attributes =
2168 object[index]->user_attributes | bit_mask;
2169 } else if (!strcmp(word[2], "hasnt")) {
2170 bit_mask = ~bit_mask;
2171 object[index]->user_attributes =
2172 object[index]->user_attributes & bit_mask;
2173 }
2174 }
2175 } else {
2176 unkattrun(3);
2177 return (exit_function(TRUE));
2178 }
2179 }
2180 } else if (!strcmp(word[0], "append")) {
2181 int first = TRUE;
2182
2183 if (word[2] == NULL) {
2184 // NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND
2185 noproprun();
2186 return (exit_function(TRUE));
2187 } else {
2188 strcpy(temp_buffer, data_directory);
2189 strcat(temp_buffer, prefix);
2190 strcat(temp_buffer, "-");
2191 strcat(temp_buffer, text_of_word(1));
2192 strcat(temp_buffer, ".csv");
2193
2194 outfile = File::openForWriting(temp_buffer);
2195
2196 if (outfile == NULL) {
2197 sprintf(error_buffer, "Failed to open file %s\n", temp_buffer);
2198 log_error(error_buffer, PLUS_STDOUT);
2199 } else {
2200 for (counter = 2; word[counter] != NULL && counter < MAX_WORDS; counter++) {
2201 output = text_of_word(counter);
2202 if (*output != 0) {
2203 if (first == FALSE) {
2204 outfile->writeByte(',');
2205 }
2206 csv_fwrite(outfile, output, (size_t) strlen(output));
2207 first = FALSE;
2208 }
2209 }
2210
2211 // TERMINATE THE LINE
2212 outfile->writeByte('\n');
2213
2214 // FLUSH AND CLOSE THE FILE
2215 outfile->flush();
2216 }
2217
2218 delete outfile;
2219 outfile = NULL;
2220 }
2221 } else if (!strcmp(word[0], "insert")) {
2222 int first = TRUE;
2223
2224 if (word[1] == NULL) {
2225 // NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND
2226 noproprun();
2227 return (exit_function(TRUE));
2228 } else {
2229 if (outfile == NULL) {
2230 log_error("Insert statement not inside an 'update' loop.", PLUS_STDOUT);
2231 } else {
2232 for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
2233 output = text_of_word(counter);
2234 if (*output != 0) {
2235 if (first == FALSE) {
2236 outfile->writeByte(',');
2237 }
2238 csv_fwrite(outfile, output, (size_t) strlen(output));
2239 first = FALSE;
2240 }
2241 }
2242
2243 // TERMINATE THE LINE
2244 outfile->writeByte('\n');
2245 }
2246 }
2247 } else if (!strcmp(word[0], "inspect")) {
2248 if (word[1] == NULL) {
2249 // NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND
2250 noproprun();
2251 return (exit_function(TRUE));
2252 } else {
2253 inspect(value_of(word[1], TRUE));
2254 }
2255 } else if (!strcmp(word[0], "move")) {
2256 /* THIS COMMAND IS USED TO MOVE AN OBJECT TO HAVE ANOTHER PARENT
2257 * INCLUDING MODIFYING ALL QUANTITY VALUES BASED ON THE OBJECTS MASS */
2258 if (word[3] == NULL) {
2259 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
2260 noproprun();
2261 return (exit_function(TRUE));
2262 }
2263
2264 index = value_of(word[1], TRUE);
2265 if (index < 1 || index > objects) {
2266 badptrrun(word[1], index);
2267 return (exit_function(TRUE));
2268 } else {
2269 object_2 = object[index]->PARENT;
2270 if (object_2 && !(object[object_2]->attributes & LOCATION)) {
2271 object[object_2]->QUANTITY += object[index]->MASS;
2272 }
2273 object_1 = value_of(word[3], TRUE);
2274 if (object_1 < 1 || object_1 > objects) {
2275 badptrrun(word[1], object_1);
2276 return (exit_function(TRUE));
2277 } else {
2278 object[index]->PARENT = object_1;
2279 if (!(object[object_1]->attributes & LOCATION))
2280 object[object_1]->QUANTITY -= object[index]->MASS;
2281 }
2282 }
2283 } else if (!strcmp(word[0], "ifstringall")) {
2284 /* CHECK IF A STRING EQUALS OR CONTAINS ANOTHER STRING */
2285 currentLevel++;
2286 if (word[3] == NULL) {
2287 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
2288 noproprun(0);
2289 return (exit_function(TRUE));
2290 } else if (and_strcondition()) {
2291 executionLevel++;
2292 }
2293 } else if (!strcmp(word[0], "ifstring")) {
2294 /* CHECK IF A STRING EQUALS OR CONTAINS ANOTHER STRING */
2295 currentLevel++;
2296 if (word[3] == NULL) {
2297 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
2298 noproprun(0);
2299 return (exit_function(TRUE));
2300 } else if (strcondition()) {
2301 executionLevel++;
2302 }
2303 } else if (!strcmp(word[0], "ifexecute")) {
2304 currentLevel++;
2305 if (word[1] == NULL) {
2306 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
2307 noproprun(0);
2308 return (exit_function(TRUE));
2309 } else {
2310 /* RESOLVE ALL THE TEXT AND STORE IT IN A TEMPORARY BUFFER*/
2311 string_buffer[0] = 0;
2312
2313 for (counter = 1; word[counter] != NULL && counter < MAX_WORDS; counter++) {
2314 strcat(string_buffer, arg_text_of_word(counter));
2315 }
2316
2317 if (execute(string_buffer)) {
2318 executionLevel++;
2319 }
2320 }
2321 } else if (!strcmp(word[0], "if")) {
2322 currentLevel++;
2323 if (word[3] == NULL) {
2324 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
2325 noproprun(0);
2326 return (exit_function(TRUE));
2327 } else if (condition()) {
2328 executionLevel++;
2329 }
2330 } else if (!strcmp(word[0], "ifall")) {
2331 currentLevel++;
2332 if (word[3] == NULL) {
2333 /* NOT ENOUGH PARAMETERS SUPPLIED FOR THIS COMMAND */
2334 noproprun(0);
2335 return (exit_function(TRUE));
2336 } else if (and_condition()) {
2337 executionLevel++;
2338 }
2339 } else {
2340 sprintf(error_buffer, UNKNOWN_COMMAND,
2341 executing_function->name, word[0]);
2342 log_error(error_buffer, PLUS_STDOUT);
2343 }
2344 } else if (!strcmp(word[wp], "if")
2345 || !strcmp(word[wp], "ifall")
2346 || !strcmp(word[wp], "ifstring")
2347 || !strcmp(word[wp], "ifstringall")
2348 || !strcmp(word[wp], "ifexecute")
2349 || !strcmp(word[wp], "iterate")
2350 || !strcmp(word[wp], "update")
2351 || !strcmp(word[wp], "while")
2352 || !strcmp(word[wp], "whileall")) {
2353 currentLevel++;
2354 }
2355
2356 #ifdef GLK
2357 if (g_vm->shouldQuit())
2358 return 0;
2359
2360 before_command = g_vm->glk_stream_get_position(game_stream);
2361 (void)glk_get_bin_line_stream(game_stream, text_buffer, (glui32) 1024);
2362 #else
2363 before_command = ftell(file);
2364 fgets(text_buffer, 1024, file);
2365 #endif
2366 if (encrypted) jacl_decrypt(text_buffer);
2367 };
2368
2369 return (exit_function(TRUE));
2370 }
2371
exit_function(int return_code)2372 int exit_function(int return_code) {
2373 if (infile != NULL) {
2374 read_lck.l_type = F_UNLCK; // SETTING A READ LOCK
2375 fcntl(read_fd, F_SETLK, &read_lck);
2376 delete infile;
2377 infile = NULL;
2378 }
2379
2380 if (outfile != NULL) {
2381 write_lck.l_type = F_UNLCK; // SETTING A WRITE LOCK
2382 fcntl(write_fd, F_SETLK, &write_lck);
2383 delete outfile;
2384 outfile = NULL;
2385 }
2386
2387 /* POP THE STACK REGARDLESS OF THE RETURN CODE */
2388 pop_stack();
2389
2390 return (return_code);
2391 }
2392
object_names(int object_index,char * names_buffer)2393 char *object_names(int object_index, char *names_buffer) {
2394 /* THIS FUNCTION CREATES A LIST OF ALL AN OBJECT'S NAMES.
2395 THE escape ARGUMENT INDICATES WHETHER A + SIGN SHOULD BE
2396 USED IN PLACE OF A SPACE BETWEEN EACH OF THE NAMES */
2397 struct name_type *current_name = object[object_index]->first_name;
2398 names_buffer[0] = 0;
2399
2400 while (current_name != NULL) {
2401 strcat(names_buffer, " ");
2402 strcat(names_buffer, current_name->name);
2403 current_name = current_name->next_name;
2404 }
2405
2406 return names_buffer;
2407 }
2408
distance(double x1,double y1,double x2,double y2)2409 int distance(double x1, double y1, double x2, double y2) {
2410 /* THIS FUNCTION CALCULATES THE DISTANCE BETWEEN TWO POINTS IN A
2411 TWO-DIMENSIONAL PLANE */
2412 double delta_x,
2413 delta_y;
2414 double distance,
2415 total;
2416
2417 /*
2418 * Object two in which quadrant compared to object one? 0 x = opp, y =
2419 * ajd + 0 degrees 1 x = adj, y = opp + 90 degrees 2 x = opp, y = ajd
2420 * + 180 degrees 3 x = adj, y = opp + 270 degrees
2421 */
2422
2423 /*
2424 * DETERMINE WHICH QUADRANT OBJECT TWO IS IN
2425 */
2426
2427 if (x2 > x1) {
2428 /*
2429 * OBJECT TWO IS IN 1 OR 2
2430 */
2431 delta_x = x2 - x1;
2432 if (y2 > y1) {
2433 delta_y = y2 - y1;
2434 } else {
2435 delta_y = y1 - y2;
2436 }
2437 } else {
2438 /*
2439 * OBJECT TWO IS IN 3 OR 4
2440 */
2441 delta_x = x1 - x2;
2442 if (y2 > y1) {
2443 delta_y = y2 - y1;
2444 } else {
2445 delta_y = y1 - y2;
2446 }
2447 }
2448
2449 delta_y = delta_y * delta_y;
2450 delta_x = delta_x * delta_x;
2451
2452 total = delta_y + delta_x;
2453
2454 distance = sqrt(total);
2455
2456 return ((int) distance);
2457 }
2458
new_position(double x1,double y1,double bearing,double velocity)2459 void new_position(double x1, double y1, double bearing, double velocity) {
2460 double delta_x,
2461 delta_y;
2462 double radians;
2463
2464 /*
2465 * Object two in which quadrant compared to object one? 0 x = opp, y =
2466 * ajd + 0 degrees 1 x = adj, y = opp + 90 degrees 2 x = opp, y = ajd
2467 * + 180 degrees 3 x = adj, y = opp + 270 degrees
2468 */
2469
2470 /*
2471 * sin finds opp, cos finds adj
2472 */
2473
2474 if (bearing < 91) {
2475 radians = bearing * 2.0 * M_PI / 360.;
2476 delta_x = velocity * sin(radians);
2477 delta_y = velocity * cos(radians);
2478 new_x = x1 + delta_x;
2479 new_y = y1 + delta_y;
2480 } else if (bearing < 181) {
2481 bearing -= 90;
2482 radians = bearing * 2.0 * M_PI / 360.;
2483 delta_y = velocity * sin(radians);
2484 delta_x = velocity * cos(radians);
2485 new_x = x1 + delta_x;
2486 new_y = y1 - delta_y;
2487 } else if (bearing < 271) {
2488 bearing -= 180;
2489 radians = bearing * 2.0 * M_PI / 360.;
2490 delta_x = velocity * sin(radians);
2491 delta_y = velocity * cos(radians);
2492 new_x = x1 - delta_x;
2493 new_y = y1 - delta_y;
2494 } else {
2495 bearing -= 270;
2496 radians = bearing * 2.0 * M_PI / 360.;
2497 delta_y = velocity * sin(radians);
2498 delta_x = velocity * cos(radians);
2499 new_x = x1 - delta_x;
2500 new_y = y1 + delta_y;
2501 }
2502 }
2503
bearing(double x1,double y1,double x2,double y2)2504 int bearing(double x1, double y1, double x2, double y2) {
2505 int quadrant;
2506 double delta_x,
2507 delta_y;
2508 double oppoadj;
2509 double bearing;
2510
2511 /*
2512 * Object two in which quadrant compared to object one? 0 x = opp, y =
2513 * ajd + 0 degrees 1 x = adj, y = opp + 90 degrees 2 x = opp, y = ajd
2514 * + 180 degrees 3 x = adj, y = opp + 270 degrees
2515 */
2516
2517 if (x2 > x1) {
2518 delta_x = x2 - x1;
2519 if (y2 > y1) {
2520 quadrant = 0;
2521 delta_y = y2 - y1;
2522 oppoadj = delta_x / delta_y;
2523 } else {
2524 quadrant = 1;
2525 delta_y = y1 - y2;
2526 oppoadj = delta_y / delta_x;
2527 }
2528 } else {
2529 delta_x = x1 - x2;
2530 if (y2 > y1) {
2531 quadrant = 3;
2532 delta_y = y2 - y1;
2533 oppoadj = delta_y / delta_x;
2534 } else {
2535 quadrant = 2;
2536 delta_y = y1 - y2;
2537 oppoadj = delta_x / delta_y;
2538 }
2539 }
2540
2541 bearing = atan(oppoadj);
2542 bearing = bearing / (2.0 * M_PI) * 360.;
2543 bearing = bearing + (90 * quadrant);
2544
2545 return ((int) bearing);
2546 }
2547
set_arguments(const char * function_call)2548 void set_arguments(const char *function_call) {
2549 /* THIS FUNCTION CREATES AN ARRAY OF JACL INTEGER CONSTANTS TO
2550 REPRESENT THE ARGUMENTS PASSED TO A JACL FUNCTION */
2551 int index,
2552 counter,
2553 length;
2554 int position = 0; /* STORE THE INDEX OF THE WORD */
2555 /* SETTING new_word TO FALSE SKIPS THE FIRST */
2556 /* WORD WHICH IS THE FUNCTION NAME */
2557 int new_word = FALSE;
2558
2559 char *arg_ptr[MAX_WORDS];
2560 int arg_value[MAX_WORDS];
2561
2562 struct integer_type *resolved_integer;
2563 struct cinteger_type *resolved_cinteger;
2564
2565 /* SPLIT UP THE FUNCTION CALL STRING AND EXTRACT THE ARGUMENTS */
2566 length = strlen(function_call);
2567
2568 for (index = 0; index < length; index++) {
2569 if (function_call[index] == '<') {
2570 argument_buffer[index] = 0;
2571 new_word = TRUE;
2572 } else {
2573 // COPY THE CHARACTER FROM THE CALLED NAME INTO THE CURRENT
2574 // ARGUMENT BUFFER
2575 argument_buffer[index] = function_call[index];
2576 if (new_word) {
2577 // THIS IS THE FIRST CHARACTER OF A NEW ARGUMENT SO STORE
2578 // THE ADDRESS OF THIS CHARACTER IN THE ARGUMENT BUFFER
2579 arg_ptr[position] = &argument_buffer[index];
2580 new_word = FALSE;
2581 if (position < MAX_WORDS)
2582 position++;
2583 }
2584 }
2585 }
2586
2587 argument_buffer[index] = 0;
2588
2589 /* CLEAR THE NEXT ARGUMENT POINTER */
2590 arg_ptr[position] = NULL;
2591
2592 /* STORE THE INTEGER VALUE OF EACH ARGUMENT PASSED*/
2593 index = 0;
2594 while (arg_ptr[index] != NULL) {
2595 //arg_value[index] = value_of(arg_ptr[index], TRUE);
2596
2597 if ((resolved_integer = integer_resolve(arg_ptr[index])) != NULL) {
2598 arg_value[index] = resolved_integer->value;
2599 } else if ((resolved_cinteger = cinteger_resolve(arg_ptr[index])) != NULL) {
2600 arg_value[index] = resolved_cinteger->value;
2601 } else if (object_element_resolve(arg_ptr[index])) {
2602 arg_value[index] = oec;
2603 } else if ((counter = object_resolve(arg_ptr[index])) != -1) {
2604 if (counter < 1 || counter > objects) {
2605 badptrrun(arg_ptr[index], counter);
2606 pop_stack();
2607 return;
2608 } else {
2609 arg_value[index] = counter;
2610 }
2611 } else if (validate(arg_ptr[index])) {
2612 arg_value[index] = atoi(arg_ptr[index]);
2613 } else {
2614 arg_value[index] = -1;
2615 }
2616
2617 index++;
2618 }
2619
2620 /* THE CURRENT ARGUMENTS HAVE ALREADY BEEN PUSHED ONTO THE STACK
2621 * AND STORED IF PASSED AS AN ARGUMENT TO THIS FUNCTION SO IT IS
2622 * OKAY TO CLEAR THEM AND SET THE NEW VALUES */
2623 clear_cinteger("arg");
2624 clear_cstring("string_arg");
2625
2626 /* CREATE A CONSTANT FOR EACH ARGUMENT AFTER THE CORE FUNCTION NAME */
2627 index = 0;
2628 while (arg_ptr[index] != NULL) {
2629 if (index == 0) noun[3] = arg_value[index];
2630 add_cinteger("arg", arg_value[index]);
2631 //printf("--- %s = %s\n", arg_ptr[index], arg_text_of(arg_ptr[index]));
2632 add_cstring("string_arg", arg_text_of(arg_ptr[index]));
2633 index++;
2634 }
2635 }
2636
pop_stack()2637 void pop_stack() {
2638 int index, counter;
2639
2640 stack--;
2641
2642 clear_cinteger("arg");
2643 clear_cstring("string_arg");
2644
2645 /* RECREATE THE arg ARRAY FOR THIS STACK FRAME */
2646 for (index = 0; index < backup[stack].argcount; index++) {
2647 if (index == 0) noun[3] = backup[stack].arguments[0];
2648 add_cinteger("arg", backup[stack].arguments[index]);
2649 }
2650
2651 /* RECREATE THE string_arg ARRAY FOR THIS STACK FRAME */
2652 for (index = 0; index < backup[stack].argcount; index++) {
2653 add_cstring("string_arg", backup[stack].str_arguments[index]);
2654 }
2655
2656 /* RESTORE THE CONTENTS OF text_buffer */
2657 for (counter = 0; counter < 1024; counter++)
2658 text_buffer[counter] = backup[stack].text_buffer[counter];
2659
2660 /* RESTORE THE CONTENTS OF called_name */
2661 //for (counter = 0; counter < 256; counter++)
2662 //called_name[counter] = backup[stack].called_name[counter];
2663 strncpy(called_name, backup[stack].called_name, 1024);
2664
2665 /* RESTORE THE CONTENTS OF scope_criterion */
2666 //for (counter = 0; counter < 21; counter++)
2667 // scope_criterion[counter] = backup[stack].scope_criterion[counter];
2668 strncpy(scope_criterion, backup[stack].scope_criterion, 20);
2669
2670 /* RESTORE THE STORED FUNCTION NAMES THAT ARE USED WHEN AN
2671 * 'override' COMMAND IS ENCOUNTERED IN THE CURRENT FUNCTION */
2672 strncpy(override_, backup[stack]._override, 80);
2673 strncpy(default_function, backup[stack].default_function, 80);
2674
2675 /* RESTORE ALL THE WORD POINTERS */
2676 for (counter = 0; counter < MAX_WORDS; counter++) {
2677 word[counter] = backup[stack].word[counter];
2678 quoted[counter] = backup[stack].quoted[counter];
2679 }
2680
2681 executing_function = backup[stack].function;
2682
2683 if (executing_function != NULL) {
2684 strncpy(function_name, executing_function->name, 80);
2685 strncpy(cstring_resolve("function_name")->value, executing_function->name, 80);
2686 }
2687
2688 wp = backup[stack].wp;
2689 top_of_loop = backup[stack].top_of_loop;
2690 outfile = backup[stack].outfile;
2691 infile = backup[stack].infile;
2692 top_of_select = backup[stack].top_of_select;
2693 top_of_while = backup[stack].top_of_while;
2694 top_of_iterate = backup[stack].top_of_iterate;
2695 top_of_update = backup[stack].top_of_update;
2696 top_of_do_loop = backup[stack].top_of_do_loop;
2697 criterion_value = backup[stack].criterion_value;
2698 criterion_type = backup[stack].criterion_type;
2699 criterion_negate = backup[stack].criterion_negate;
2700 current_level = backup[stack].current_level;
2701 execution_level = backup[stack].execution_level;
2702 loop_integer = backup[stack].loop_integer;
2703 select_integer = backup[stack].select_integer;
2704
2705 #ifdef GLK
2706 g_vm->glk_stream_set_position(game_stream, backup[stack].address, seekmode_Start);
2707 #else
2708 fseek(file, backup[stack].address, SEEK_SET);
2709 #endif
2710
2711 }
2712
push_stack(int32 file_pointer)2713 void push_stack(int32 file_pointer) {
2714 /* COPY ALL THE CURRENT SYSTEM DATA ONTO THE STACK */
2715 int index;
2716 int counter = 0;
2717
2718 if (stack == STACK_SIZE) {
2719 log_error("Stack overflow.", PLUS_STDERR);
2720 terminate(45);
2721 return;
2722 } else {
2723 backup[stack].infile = infile;
2724 infile = NULL;
2725 backup[stack].outfile = outfile;
2726 outfile = NULL;
2727 backup[stack].function = executing_function;
2728 backup[stack].address = file_pointer;
2729 backup[stack].wp = wp;
2730 backup[stack].top_of_loop = top_of_loop;
2731 backup[stack].top_of_select = top_of_select;
2732 backup[stack].top_of_while = top_of_while;
2733 backup[stack].top_of_iterate = top_of_iterate;
2734 backup[stack].top_of_update = top_of_update;
2735 backup[stack].top_of_do_loop = top_of_do_loop;
2736 backup[stack].criterion_value = criterion_value;
2737 backup[stack].criterion_type = criterion_type;
2738 backup[stack].criterion_negate = criterion_negate;
2739 backup[stack].current_level = current_level;
2740 backup[stack].execution_level = execution_level;
2741 backup[stack].loop_integer = loop_integer;
2742 backup[stack].select_integer = select_integer;
2743
2744 /* MAKE A COPY OF THE CURRENT CONTENTS OF text_buffer */
2745 for (counter = 0; counter < 1024; counter++)
2746 backup[stack].text_buffer[counter] = text_buffer[counter];
2747
2748 /* MAKE A COPY OF THE CURRENT CONTENTS OF called_name */
2749 strncpy(backup[stack].called_name, called_name, 1024);
2750
2751 // MAKE A COPY OF THE CURRENT CONTENTS OF scope_criterion
2752 strncpy(backup[stack].scope_criterion, scope_criterion, 20);
2753
2754 /* COPY THE STORED FUNCTION NAMES THAT ARE USED WHEN AN
2755 * 'override' COMMAND IS ENCOUNTERED IN THE CURRENT FUNCTION */
2756 strncpy(backup[stack]._override, override_, 80);
2757 strncpy(backup[stack].default_function, default_function, 80);
2758
2759 /* PUSH ALL THE WORD POINTERS ONTO THE STACK */
2760 for (counter = 0; counter < MAX_WORDS; counter++) {
2761 backup[stack].word[counter] = word[counter];
2762 backup[stack].quoted[counter] = quoted[counter];
2763 }
2764
2765 // PUSH ALL THE ARGUMENTS AS INTEGERS ONTO THE STACK
2766 index = 0;
2767 current_cinteger = cinteger_table;
2768
2769 if (current_cinteger != NULL) {
2770 do {
2771 if (!strcmp(current_cinteger->name, "arg")) {
2772 backup[stack].arguments[index++] = current_cinteger->value;
2773 }
2774 current_cinteger = current_cinteger->next_cinteger;
2775 } while (current_cinteger != NULL);
2776 }
2777
2778 // STORE THE NUMBER OF ARGUMENTS PASSED TO THIS FUNCTION
2779 // THIS IS THE SAME NUMBER FOR STRINGS AND INTEGERS
2780 backup[stack].argcount = index;
2781
2782 // PUSH ALL THE ARGUMENTS AS STRINGS STRING ONTO THE STACK
2783 index = 0;
2784 current_cstring = cstring_table;
2785
2786 if (current_cstring != NULL) {
2787 do {
2788 if (!strcmp(current_cstring->name, "string_arg")) {
2789 strncpy(backup[stack].str_arguments[index++], current_cstring->value, 255);
2790 }
2791
2792 current_cstring = current_cstring->next_string;
2793 } while (current_cstring != NULL);
2794 }
2795 }
2796
2797 // PUSH ON TO THE NEXT STACK FRAME
2798 stack++;
2799 }
2800
pop_proxy()2801 void pop_proxy() {
2802 int index, counter;
2803
2804 proxy_stack--;
2805
2806 clear_cinteger("$integer");
2807 clear_cstring("$string");
2808 clear_cstring("$word");
2809
2810 /* RECREATE THE integer ARRAY FOR THIS STACK FRAME */
2811 for (index = 0; index < proxy_backup[proxy_stack].integercount; index++) {
2812 add_cinteger("$integer", proxy_backup[proxy_stack].integer[index]);
2813 }
2814
2815 /* RECREATE THE text ARRAY FOR THIS STACK FRAME */
2816 for (index = 0; index < proxy_backup[proxy_stack].textcount; index++) {
2817 add_cstring("$string", proxy_backup[proxy_stack].text[index]);
2818 }
2819
2820 /* RECREATE THE $word ARRAY FOR THIS STACK FRAME */
2821 for (index = 0; index < proxy_backup[proxy_stack].commandcount; index++) {
2822 add_cstring("$word", proxy_backup[proxy_stack].command[index]);
2823 }
2824
2825 /* RESTORE ALL THE NOUN POINTERS */
2826 for (counter = 0; counter < 4; counter++)
2827 noun[counter] = proxy_backup[proxy_stack].object_pointers[counter];
2828
2829 /* PUSH ALL THE RESOLVED OBJECTS ONTO THE STACK */
2830 for (index = 0; index < 4; index++) {
2831 list_size[index] = proxy_backup[proxy_stack].list_size[index];
2832 max_size[index] = proxy_backup[proxy_stack].max_size[index];
2833 for (counter = 0; counter < max_size[index]; counter++) {
2834 object_list[index][counter] = proxy_backup[proxy_stack].object_list[index][counter];
2835 }
2836 }
2837
2838 start_of_this_command = proxy_backup[proxy_stack].start_of_this_command;
2839 start_of_last_command = proxy_backup[proxy_stack].start_of_last_command;
2840 after_from = proxy_backup[proxy_stack].after_from;
2841 last_exact = proxy_backup[proxy_stack].last_exact;
2842 }
2843
push_proxy()2844 void push_proxy() {
2845 /* COPY ALL THE CURRENT SYSTEM DATA ONTO THE STACK */
2846 int index;
2847 int counter = 0;
2848 int command = 0;
2849 int text = 0;
2850
2851 current_cinteger = cinteger_table;
2852 current_cstring = cstring_table;
2853
2854 if (proxy_stack == STACK_SIZE) {
2855 log_error("Stack overflow.", PLUS_STDERR);
2856 terminate(45);
2857 return;
2858 } else {
2859 proxy_backup[proxy_stack].start_of_this_command = start_of_this_command;
2860 proxy_backup[proxy_stack].start_of_last_command = start_of_last_command;
2861
2862 /* PUSH ALL THE OBJECT POINTERS ONTO THE STACK */
2863 for (counter = 0; counter < 4; counter++)
2864 proxy_backup[proxy_stack].object_pointers[counter] = noun[counter];
2865
2866 /* PUSH ALL THE RESOLVED OBJECTS ONTO THE STACK */
2867 for (index = 0; index < 4; index++) {
2868 for (counter = 0; counter < max_size[index]; counter++) {
2869 proxy_backup[proxy_stack].object_list[index][counter]
2870 = object_list[index][counter];
2871 }
2872 proxy_backup[proxy_stack].list_size[index] = list_size[index];
2873 proxy_backup[proxy_stack].max_size[index] = max_size[index];
2874 }
2875
2876 /* PUSH ALL THE CURRENT COMMAND INTEGERS ONTO THE STACK */
2877 counter = 0;
2878
2879 if (current_cinteger != NULL) {
2880 do {
2881 if (!strcmp(current_cinteger->name, "$integer")) {
2882 proxy_backup[proxy_stack].integer[counter++] = current_cinteger->value;
2883 }
2884 current_cinteger = current_cinteger->next_cinteger;
2885 } while (current_cinteger != NULL);
2886 }
2887
2888 proxy_backup[proxy_stack].integercount = counter;
2889
2890 // PUSH ALL THE TEXT STRING SUPPLIED BY THE CURRENT COMMAND ONTO THE STACK
2891 text = 0;
2892 command = 0;
2893
2894 if (current_cstring != NULL) {
2895 do {
2896 if (!strcmp(current_cstring->name, "$string")) {
2897 strncpy(proxy_backup[proxy_stack].text[text++], current_cstring->value, 255);
2898 proxy_backup[proxy_stack].text[counter++][255] = 0;
2899 } else if (!strcmp(current_cstring->name, "$word")) {
2900 strncpy(proxy_backup[proxy_stack].command[command++], current_cstring->value, 255);
2901 }
2902
2903 current_cstring = current_cstring->next_string;
2904 } while (current_cstring != NULL);
2905 }
2906
2907 proxy_backup[proxy_stack].textcount = counter;
2908 proxy_backup[proxy_stack].commandcount = command;
2909 proxy_backup[proxy_stack].after_from = after_from;
2910 proxy_backup[proxy_stack].last_exact = last_exact;
2911 }
2912
2913 // PUSH ON TO THE NEXT STACK FRAME
2914 proxy_stack++;
2915 }
2916
condition()2917 int condition() {
2918 /* COMPARE GROUPS OF TWO ELEMENTS. RETURN TRUE IF ANY ONE GROUP OF
2919 * ELEMENTS COMPARE 'TRUE' */
2920 int first;
2921
2922 first = 1;
2923
2924 while (word[first + 2] != NULL && ((first + 2) < MAX_WORDS)) {
2925 if (logic_test(first))
2926 return (TRUE);
2927 else
2928 first = first + 3;
2929 }
2930 return (FALSE);
2931 }
2932
and_condition()2933 int and_condition() {
2934 /* COMPARE GROUPS OF TWO ELEMENTS. RETURN FALSE IF ANY ONE GROUP OF
2935 * ELEMENTS COMPARE 'FALSE' */
2936 int first;
2937
2938 first = 1;
2939
2940 while (word[first + 2] != NULL && ((first + 2) < MAX_WORDS)) {
2941 if (logic_test(first) == FALSE)
2942 return (FALSE);
2943 else
2944 first = first + 3;
2945 }
2946 return (TRUE);
2947 }
2948
logic_test(int first)2949 int logic_test(int first) {
2950 long index,
2951 compare;
2952
2953 resolved_attribute = FALSE;
2954
2955 index = value_of(word[first], TRUE);
2956 compare = value_of(word[first + 2], TRUE);
2957
2958 if (!strcmp(word[first + 1], "=") || !strcmp(word[first + 1], "==")) {
2959 if (index == compare)
2960 return (TRUE);
2961 else
2962 return (FALSE);
2963 } else if (!strcmp(word[first + 1], ">")) {
2964 if (index > compare)
2965 return (TRUE);
2966 else
2967 return (FALSE);
2968 } else if (!strcmp(word[first + 1], "<")) {
2969 if (index < compare)
2970 return (TRUE);
2971 else
2972 return (FALSE);
2973 } else if (!strcmp(word[first + 1], "is")) {
2974 if (index < 1 || index > objects) {
2975 unkobjrun(first);
2976 return (FALSE);
2977 } else
2978 return (scope(index, word[first + 2]));
2979 } else if (!strcmp(word[first + 1], "isnt")) {
2980 if (index < 1 || index > objects) {
2981 unkobjrun(first);
2982 return (FALSE);
2983 } else
2984 return (!scope(index, word[first + 2]));
2985 } else if (!strcmp(word[first + 1], "has"))
2986 if (index < 1 || index > objects) {
2987 unkobjrun(first);
2988 return (FALSE);
2989 } else {
2990 if (resolved_attribute == SYSTEM_ATTRIBUTE) {
2991 return (object[index]->attributes & compare);
2992 } else {
2993 return (object[index]->user_attributes & compare);
2994 }
2995 }
2996 else if (!strcmp(word[first + 1], "hasnt"))
2997 if (index < 1 || index > objects) {
2998 unkobjrun(first);
2999 return (FALSE);
3000 } else {
3001 if (resolved_attribute == SYSTEM_ATTRIBUTE) {
3002 return (!(object[index]->attributes & compare));
3003 } else {
3004 return (!(object[index]->user_attributes & compare));
3005 }
3006 }
3007 else if (!strcmp(word[first + 1], "!=")
3008 || !strcmp(word[first + 1], "<>")) {
3009 if (index != compare)
3010 return (TRUE);
3011 else
3012 return (FALSE);
3013 } else if (!strcmp(word[first + 1], ">=")
3014 || !strcmp(word[first + 1], "=>")) {
3015 if (index >= compare)
3016 return (TRUE);
3017 else
3018 return (FALSE);
3019 } else if (!strcmp(word[first + 1], "<=")
3020 || !strcmp(word[first + 1], "=<")) {
3021 if (index <= compare)
3022 return (TRUE);
3023 else
3024 return (FALSE);
3025 } else if (!strcmp(word[first + 1], "grandof")) {
3026 /* GRANDOF SAYS THAT AN OBJECT IS THE EVENTUAL PARENT OF ANOTHER OBJECT, NOT
3027 * NECESSARILY IMMEDIATE */
3028 if (index < 1 || index > objects) {
3029 unkobjrun(first);
3030 return (FALSE);
3031 } else {
3032 if (compare < 1 || compare > objects) {
3033 unkobjrun(first + 2);
3034 return (FALSE);
3035 } else {
3036 if (parent_of(index, compare, UNRESTRICT))
3037 return (TRUE);
3038 else
3039 return (FALSE);
3040 }
3041 }
3042 } else if (!strcmp(word[first + 1], "!grandof")) {
3043 if (index < 1 || index > objects) {
3044 unkobjrun(first);
3045 return (FALSE);
3046 } else {
3047 if (compare < 1 || compare > objects) {
3048 unkobjrun(first + 2);
3049 return (FALSE);
3050 } else {
3051 if (parent_of(index, compare, UNRESTRICT))
3052 return (FALSE);
3053 else
3054 return (TRUE);
3055 }
3056 }
3057 } else {
3058 sprintf(error_buffer,
3059 "ERROR: In function \"%s\", illegal operator \"%s\".^",
3060 executing_function->name, word[2]);
3061 write_text(error_buffer);
3062 return (FALSE);
3063 }
3064 }
3065
strcondition()3066 int strcondition() {
3067 int first;
3068
3069 first = 1;
3070
3071 while (word[first + 2] != NULL && ((first + 2) < MAX_WORDS)) {
3072 if (str_test(first))
3073 return (TRUE);
3074 else
3075 first = first + 3;
3076 }
3077 return (FALSE);
3078 }
3079
and_strcondition()3080 int and_strcondition() {
3081 int first;
3082
3083 first = 1;
3084
3085 while (word[first + 2] != NULL && ((first + 2) < MAX_WORDS)) {
3086 if (str_test(first) == FALSE)
3087 return (FALSE);
3088 else
3089 first = first + 3;
3090 }
3091 return (TRUE);
3092 }
3093
str_test(int first)3094 int str_test(int first) {
3095 const char *index;
3096 const char *compare;
3097
3098 // GET THE TWO STRING VALUES TO COMPARE
3099
3100 index = arg_text_of_word(first);
3101 compare = arg_text_of_word(first + 2);
3102
3103 if (!strcmp(word[first + 1], "==") || !strcmp(word[first + 1], "=")) {
3104 if (!scumm_stricmp(index, compare)) {
3105 return (TRUE);
3106 } else {
3107 return (FALSE);
3108 }
3109 } else if (!strcmp(word[first + 1], "!contains")) {
3110 if (strcasestr(index, compare))
3111 return (FALSE);
3112 else
3113 return (TRUE);
3114 } else if (!strcmp(word[first + 1], "contains")) {
3115 if (strcasestr(index, compare))
3116 return (TRUE);
3117 else
3118 return (FALSE);
3119 } else if (!strcmp(word[first + 1], "<>") || !strcmp(word[first + 1], "!=")) {
3120 if (scumm_stricmp(index, compare))
3121 return (TRUE);
3122 else
3123 return (FALSE);
3124 } else if (!strcmp(word[first + 1], "==C") || !strcmp(word[first + 1], "=C")) {
3125 if (!strcmp(index, compare)) {
3126 return (TRUE);
3127 } else {
3128 return (FALSE);
3129 }
3130 } else if (!strcmp(word[first + 1], "!containsC")) {
3131 if (strstr(index, compare))
3132 return (FALSE);
3133 else
3134 return (TRUE);
3135 } else if (!strcmp(word[first + 1], "containsC")) {
3136 if (strstr(index, compare))
3137 return (TRUE);
3138 else
3139 return (FALSE);
3140 } else if (!strcmp(word[first + 1], "<>C") || !strcmp(word[first + 1], "!=C")) {
3141 if (strcmp(index, compare))
3142 return (TRUE);
3143 else
3144 return (FALSE);
3145 } else {
3146 sprintf(error_buffer,
3147 "ERROR: In function \"%s\", illegal operator \"%s\".^",
3148 executing_function->name, word[2]);
3149 write_text(error_buffer);
3150 return (FALSE);
3151 }
3152 }
3153
add_cinteger(const char * name,int value)3154 void add_cinteger(const char *name, int value) {
3155 /* THIS FUNCTION ADDS A NEW JACL CONSTANT TO THE LIST */
3156
3157 if ((new_cinteger = (struct cinteger_type *)
3158 malloc(sizeof(struct cinteger_type))) == NULL)
3159 outofmem();
3160 else {
3161 if (cinteger_table == NULL) {
3162 cinteger_table = new_cinteger;
3163 } else {
3164 /* FIND LAST CONSTANT IN LIST */
3165 current_cinteger = cinteger_table;
3166 while (current_cinteger->next_cinteger != NULL) {
3167 current_cinteger = current_cinteger->next_cinteger;
3168 }
3169 current_cinteger->next_cinteger = new_cinteger;
3170 }
3171 strncpy(new_cinteger->name, name, 40);
3172 new_cinteger->name[40] = 0;
3173 new_cinteger->value = value;
3174 new_cinteger->next_cinteger = NULL;
3175 }
3176 }
3177
clear_cinteger(const char * name)3178 void clear_cinteger(const char *name) {
3179 /* FREE CONSTANTS THAT HAVE SUPPLIED NAME*/
3180
3181 //printf("--- clear integer %s\n", name);
3182 if (cinteger_table != NULL) {
3183 current_cinteger = cinteger_table;
3184 previous_cinteger = cinteger_table;
3185 while (current_cinteger != NULL) {
3186 //sprintf(temp_buffer, "--- checking integer %s^", current_cinteger->name);
3187 //write_text(temp_buffer);
3188 if (!strcmp(current_cinteger->name, name)) {
3189 //sprintf(temp_buffer, "--- found integer %s^", name);
3190 //write_text(temp_buffer);
3191 /* FREE THIS CONSTANT */
3192 if (previous_cinteger == current_cinteger) {
3193 // THE INTEGER BEING CLEARED IS THE FIRST INTEGER IN THE LIST
3194 cinteger_table = current_cinteger->next_cinteger;
3195 previous_cinteger = current_cinteger->next_cinteger;
3196 free(current_cinteger);
3197 current_cinteger = previous_cinteger;
3198 } else {
3199 previous_cinteger->next_cinteger = current_cinteger->next_cinteger;
3200 free(current_cinteger);
3201 current_cinteger = previous_cinteger->next_cinteger;
3202 }
3203 } else {
3204 previous_cinteger = current_cinteger;
3205 current_cinteger = current_cinteger->next_cinteger;
3206 }
3207 }
3208 }
3209 //printf("--- leaving clear integer\n");
3210 }
3211
add_cstring(const char * name,const char * value)3212 void add_cstring(const char *name, const char *value) {
3213 /* ADD A STRING CONSTANT WITH THE SUPPLIED NAME AND VALUE */
3214
3215 if ((new_string = (struct string_type *)
3216 malloc(sizeof(struct string_type))) == NULL)
3217 outofmem();
3218 else {
3219 if (cstring_table == NULL) {
3220 cstring_table = new_string;
3221 } else {
3222 /* FIND LAST STRING IN LIST */
3223 current_cstring = cstring_table;
3224 while (current_cstring->next_string != NULL) {
3225 current_cstring = current_cstring->next_string;
3226 }
3227 current_cstring->next_string = new_string;
3228 }
3229 strncpy(new_string->name, name, 40);
3230 new_string->name[40] = 0;
3231 strncpy(new_string->value, value, 255);
3232 new_string->value[255] = 0;
3233 new_string->next_string = NULL;
3234 }
3235 }
3236
clear_cstring(const char * name)3237 void clear_cstring(const char *name) {
3238 /* FREE CONSTANTS THAT HAVE SUPPLIED NAME*/
3239 if (cstring_table != NULL) {
3240 current_cstring = cstring_table;
3241 previous_cstring = cstring_table;
3242 while (current_cstring != NULL) {
3243 if (!strcmp(current_cstring->name, name)) {
3244 /* FREE THIS STRING */
3245 if (previous_cstring == current_cstring) {
3246 cstring_table = current_cstring->next_string;
3247 previous_cstring = current_cstring->next_string;
3248 free(current_cstring);
3249 current_cstring = previous_cstring;
3250 } else {
3251 previous_cstring->next_string = current_cstring->next_string;
3252 free(current_cstring);
3253 current_cstring = previous_cstring->next_string;
3254 }
3255 } else {
3256 previous_cstring = current_cstring;
3257 current_cstring = current_cstring->next_string;
3258 }
3259 }
3260 }
3261 }
3262
inspect(int object_num)3263 void inspect(int object_num) {
3264 // THIS FUNCTION DISPLAYS THE STATE OF A JACL OBJECT FOR DEBUGGING
3265
3266 int index, attribute_value;
3267
3268 struct attribute_type *pointer = attribute_table;
3269
3270 if (object_num < 1 || object_num > objects) {
3271 badptrrun(word[1], object_num);
3272 return;
3273 }
3274
3275 write_text("label: ");
3276 write_text(object[object_num]->label);
3277
3278 if (object[object_num]->attributes & LOCATION) {
3279 // OUTPUT ALL THE ATTRIBUTES WITH LOCATION ATTRIBUTE TEXT
3280 write_text("^has location attributes: ");
3281 index = 0;
3282 attribute_value = 1;
3283 while (location_attributes[index] != NULL) {
3284 if (object[object_num]->attributes & attribute_value) {
3285 write_text(location_attributes[index]);
3286 }
3287 index++;
3288 attribute_value *= 2;
3289 }
3290 } else {
3291 // OUTPUT ALL THE ATTRIBUTES WITH OBJECT ATTRIBUTE TEXT
3292 write_text("^has object attributes: ");
3293 index = 0;
3294 attribute_value = 1;
3295 while (object_attributes[index] != NULL) {
3296 if (object[object_num]->attributes & attribute_value) {
3297 write_text(object_attributes[index]);
3298 }
3299 index++;
3300 attribute_value *= 2;
3301 }
3302
3303 write_text("^has user attributes: ");
3304 attribute_value = 1;
3305 }
3306
3307 if (pointer != NULL) {
3308 // THERE ARE USER ATTRIBUTES, SO CHECK IF THIS OBJECT OR LOCATION
3309 // HAS ANY OF THEM
3310 do {
3311 if (object[object_num]->user_attributes & pointer->value) {
3312 write_text(pointer->name);
3313 write_text(" ");
3314 }
3315
3316 pointer = pointer->next_attribute;
3317 } while (pointer != NULL);
3318 }
3319
3320 write_text("^");
3321
3322 index = 0;
3323 if (object[object_num]->attributes & LOCATION) {
3324 while (location_elements[index] != NULL) {
3325 if (index < 12) {
3326 if (object[object_num]->integer[index] < 1 || object[object_num]->integer[index] > objects) {
3327 sprintf(temp_buffer, "%s: nowhere (%d)^", location_elements[index], object[object_num]->integer[index]);
3328 } else {
3329 sprintf(temp_buffer, "%s: %s (%d)^", location_elements[index], object[object[object_num]->integer[index]]->label, object[object_num]->integer[index]);
3330 }
3331 } else {
3332 sprintf(temp_buffer, "%s: %d^", location_elements[index], object[object_num]->integer[index]);
3333 }
3334 write_text(temp_buffer);
3335 index++;
3336 }
3337 } else {
3338 while (object_elements[index] != NULL) {
3339 if (index == 0) {
3340 sprintf(temp_buffer, "%s: %s (%d)^", object_elements[index], object[object[object_num]->integer[index]]->label, object[object_num]->integer[index]);
3341 } else {
3342 sprintf(temp_buffer, "%s: %d^", object_elements[index], object[object_num]->integer[index]);
3343 }
3344 write_text(temp_buffer);
3345 index++;
3346 }
3347 }
3348 }
3349
grand_of(int child,int objs_only)3350 int grand_of(int child, int objs_only) {
3351 /* THIS FUNCTION WILL CLIMB THE OBJECT TREE STARTING AT 'CHILD' UNTIL
3352 * A 'PARENT' IS REACHED */
3353
3354 /* objs_only ARGUMENT TELLS FUNCTION TO IGNORE OBJECT IF IT IS IN A
3355 * LOCATION */
3356
3357 int parent;
3358
3359 if (object[child]->PARENT != NOWHERE) {
3360 /* STORE THE CHILDS PARENT OBJECT */
3361 parent = object[child]->PARENT;
3362
3363 if (object[parent]->attributes & LOCATION) {
3364 if (objs_only) {
3365 /* THE CHILDS PARENT IS LOCATION AND SEARCH IS RESTRICTED TO
3366 * OBJECTS */
3367 return (child);
3368 } else {
3369 return (parent);
3370 }
3371 } else {
3372 /* KEEP LOOKING UP THE TREE UNTIL THE CHILD HAS NO
3373 * PARENT */
3374 return (grand_of(parent, objs_only));
3375 }
3376 } else {
3377 /* THE SPECIFIED OBJECT HAS NO PARENT */
3378 return (child);
3379 }
3380 }
3381
select_next()3382 int select_next() {
3383 while (++*select_integer <= objects) {
3384 switch (criterion_type) {
3385 case CRI_ATTRIBUTE:
3386 if (object[*select_integer]->attributes & criterion_value) {
3387 if (!criterion_negate) {
3388 return TRUE;
3389 }
3390 } else {
3391 if (criterion_negate) {
3392 return TRUE;
3393 }
3394 }
3395 break;
3396 case CRI_USER_ATTRIBUTE:
3397 if (object[*select_integer]->user_attributes & criterion_value) {
3398 if (!criterion_negate) {
3399 return TRUE;
3400 }
3401 } else {
3402 if (criterion_negate) {
3403 return TRUE;
3404 }
3405 }
3406 break;
3407 case CRI_PARENT:
3408 if (object[*select_integer]->PARENT == criterion_value) {
3409 if (!criterion_negate) {
3410 return TRUE;
3411 }
3412 } else {
3413 if (criterion_negate) {
3414 return TRUE;
3415 }
3416 }
3417 break;
3418 case CRI_SCOPE:
3419 if (scope(*select_integer, scope_criterion)) {
3420 if (!criterion_negate) {
3421 return TRUE;
3422 }
3423 } else {
3424 if (criterion_negate) {
3425 return TRUE;
3426 }
3427 }
3428 break;
3429 default:
3430 break;
3431 }
3432 }
3433
3434 return (FALSE);
3435 }
3436
3437 /* Converts an integer value to its hex character*/
to_hex(char code)3438 char to_hex(char code) {
3439 static char hex[] = "0123456789abcdef";
3440 return hex[code & 15];
3441 }
3442
3443 /* Returns a url-encoded version of str */
3444 /* IMPORTANT: be sure to free() the returned string after use */
url_encode(char * str)3445 char *url_encode(char *str) {
3446 char *pstr = str, *buf = (char *)malloc(strlen(str) * 3 + 1), *pbuf = buf;
3447 while (*pstr) {
3448 if (Common::isAlnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
3449 *pbuf++ = *pstr;
3450 else if (*pstr == ' ')
3451 *pbuf++ = '+';
3452 else
3453 *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
3454 pstr++;
3455 }
3456 *pbuf = '\0';
3457 return buf;
3458 }
3459
3460 } // End of namespace JACL
3461 } // End of namespace Glk
3462