aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/jacl/parser.cpp
blob: d37f4d1f4b2d6bbe90bdf0f38e647c2b4cc8e12c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "glk/jacl/jacl.h"
#include "glk/jacl/language.h"
#include "glk/jacl/types.h"
#include "glk/jacl/prototypes.h"

namespace Glk {
namespace JACL {

#define FIRST_LIST      noun_number-2
#define SECOND_LIST     noun_number

int                             object_list[4][MAX_OBJECTS];
int                             multiple_resolved[MAX_OBJECTS];

// THIS IS THE NUMBER OF OBJECTS LEFT IN THE LIST
int                             list_size[4];

// THIS IS THE INDEX OF THE FINAL OBJECT
int                             max_size[4];

/* object_list[] HAS THE FOLLOWING INDEXES:
 * noun1            : 0
 * noun2            : 1
 * noun1 EXCEPTIONS : 2
 * noun2 EXCEPTIONS : 3
 */

int                             selection;

int                             matches = 0;
int                             highest_confidence = 0;
int                             prime_suspect = 0;
int                             done = 0;
int                             backup_pointer = 0;
int                             everything = 0;

int                             confidence[MAX_OBJECTS];
int                             possible_objects[MAX_OBJECTS];

int                             it;
int                             them[MAX_OBJECTS];
int                             her;
int                             him;
int                             parent;

int                             custom_error;

int                             oops_word;
int                             last_exact;

char                            *expected_scope[3];

// THIS ARRAY DEFINES THE OBJECTS THAT THE CURRENT OBJECTS ARE
// SUPPOSED TO BE A CHILD OF
int                             from_objects[MAX_OBJECTS];

int                             after_from;
const char                      *from_word;

int                             object_expected = FALSE;

char                            default_function[84];
char                            object_name[84];

char                            base_function[84];
char                            before_function[84];
char                            after_function[84];
char                            local_after_function[84];

extern char                     text_buffer[];
extern char                     function_name[];
extern char                     temp_buffer[];
extern char                     error_buffer[];
extern char                     override_[];
extern const char               *word[];

extern int                      quoted[];

extern struct object_type       *object[];
extern int                      objects;

extern int                      noun[];
extern int                      wp;
extern int                      player;

extern struct word_type         *grammar_table;
extern struct function_type     *executing_function;
extern struct object_type       *object[];
extern struct variable_type     *variable[];

void parser() {
	// THIS FUNCTION COMPARES THE WORDS IN THE PLAYER'S COMMAND TO THE
	// GRAMMAR TREE OF POSSIBLE COMMANDS

	struct word_type *pointer;
	struct word_type *matched_word = NULL;

	int             index;
	int             current_noun = 0;

	// RESET TO START OF PROCESSING

	////printf("--- in parser\n");

	// THIS IS USED TO STORE THE LAST EXACT WORD MATCH IN THE PLAYERS COMMAND
	last_exact = -1;
	after_from = -1;
	from_objects[0] = 0;

	noun[0] = FALSE;
	noun[1] = FALSE;

	// RESET BOTH THE LISTS TO BE EMPTY
	//printf("--- reset lists\n");
	for (index = 0; index < 4; index++) {
		list_size[index] = 0;
		max_size[index] = 0;
	}

	//printf("--- clear $integer\n");
	clear_cinteger("$integer");
	//printf("--- clear $string\n");
	clear_cstring("$string");
	//printf("--- clear action\n");
	clear_cstring("action");

	if (grammar_table == NULL) {
		// THERE ARE NO USER DEFINED COMMANDS AVAILABLE, SO THE USER'S
		// COMMAND IS INEVITABLY INVALID
		//printf("--- no grammar table\n");
		INTERRUPTED->value = TRUE;
		diagnose();
		return;
	}

	//printf("--- set pointer to grammar table\n");
	// START AT THE TOP OF THE GRAMMAR TREE
	pointer = grammar_table;

	while (word[wp] != NULL && pointer != NULL) {
		//printf("--- wp = %d\n", wp);
		//printf("--- word[%d] = %s\n", wp, word[wp]);
		object_expected = FALSE;

		if (!strcmp(cstring_resolve("THEN_WORD")->value, word[wp])) {
			// CONSIDER THIS THE END OF THIS COMMAND AS 'THEN' IS
			// TREATED LIKE A FULL STOP
			break;
		} else if ((matched_word = exact_match(pointer)) != NULL) {
			// THIS WORD WAS AN EXACT MATCH FOR ONE OF THE POSSIBLE WORDS
			// AT THE CURRENT GRAMMAR TREE LEVEL - MOVE ON!
			pointer = matched_word;
			pointer = pointer->first_child;
		} else if ((matched_word = object_match(pointer, current_noun)) != NULL) {
			// THIS WAS AN OBJECT PLACE HOLDER AT THIS GRAMMAR LEVEL AND
			// THIS POINT IN THE PLAYER'S COMMAND COULD BE RESOLVED TO
			// AT LEAST ONE OBJECT

			if (list_size[current_noun] > 1) {
				// MULTIPLE OBJECTS WERE RETURNED
				if (matched_word->word[1] != '*') {
					if (last_exact == -1) {
						write_text(cstring_resolve("NO_MULTI_START")->value);
					} else {
						sprintf(error_buffer, cstring_resolve("NO_MULTI_VERB")->value, word[last_exact]);
						write_text(error_buffer);
					}

					INTERRUPTED->value = TRUE;
					return;
				}
			}

			// STORE THE EXPECTED SCOPE FOR LATER CHECKING
			expected_scope[current_noun] = (char *)&matched_word->word;
			//printf("--- expected scope for noun%d is %s\n", current_noun, expected_scope[current_noun]);

			// THE NEXT MATCH OR GROUP OF MATCHES SHOULD BE IN THE SECOND
			// LIST OF OBJECTS
			current_noun++;

			// PUSH ON FROM THE POINT A MATCH WAS FOUND....
			pointer = matched_word;
			pointer = pointer->first_child;
		} else {
			// THIS IS AN UNKNOWN WORD
			if (oops_word == -1 && word[wp] != NULL) {
				oops_word = wp;
			}

			if (custom_error == TRUE) {
				// THERE HAS BEEN SOME CUSTOM ERROR DISPLAYED ALREADY
				// SO JUST RETURN
				TIME->value = FALSE;
				INTERRUPTED->value = TRUE;
				return;
			} else {
				// THERE ARE NO MORE POSIBILITIES, THE WORD CAN'T BE
				// USED IN THIS CONTEXT
				INTERRUPTED->value = TRUE;
				diagnose();
				return;
			}
		}
	};

	if (pointer == NULL) {
		// THIS CAN ONLY HAPPEN IF MOVING THE POINTER TO ITS
		// FIRST CHILD RESULTS IN A NULL - AN INCOMPLETE
		// GRAMMAR STATEMENT.
		log_error(INCOMPLETE_GRAMMAR, PLUS_STDOUT);
		INTERRUPTED->value = TRUE;
		return;
	}

	//printf("--- first list has %d objects\n", list_size[0]);
	//printf("--- first list has %d max\n", max_size[0]);
	//printf("--- second list has %d objects\n", list_size[1]);
	//printf("--- second list has %d max\n", max_size[1]);

	/* THE PLAYER'S MOVE HAS NO MORE WORDS, AND A BRANCH OF
	 * THE GRAMMAR TREE HAS BEEN FOUND THAT SO FAR MATCHES
	 * THIS MOVE. SCAN THROUGH ALL THE CHILDREN OF THE FINAL
	 * WORD OF THE PLAYER'S COMMAND AND FIND A FUNCTION BASE.
	 * (FUNCTION BASES BEGIN WITH '>'). IF THIS IS FOUND,
	 * CALL THE APPROPRIATE JACL FUNCTIONS IN THE GAME CODE
	 * IF NO FUNCTION BASE IS FOUND, MORE WORDS WERE EXPECTED
	 * IN ORDER TO CONSTRUCT A COMPLETE COMMAND. SHOW ERROR. */
	do {
		if (pointer->word[0] == '>') {
			//printf("--- found action %s\n", pointer->word);
			/* CALL ALL THE APPROPRIATE FUNCTIONS FOR EACH OF */
			/* THE OBJECTS IN THE SET */
			add_cstring("action", &pointer->word[1]);

			if (list_size[0] > 1) {
				/* FIRST IS MULTI, PRESUME SECOND IS SINGLE OR ZERO AS YOU
				 * CAN HAVE COMMANDS WITH TWO MULTIPLE OBJECT REFERENCES */
				noun[1] = first_available(1);

				for (index = 0; index < max_size[0]; index++) {
					/* CALL ALL THE FUNCTIONS ONCE FOR EACH OJECT */
					if (object_list[0][index] != 0) {
						noun[0] = object_list[0][index];

						if (MULTI_PREFIX->value) {
							// DISPLAY THE OBJECT BEING ACTED ON
							plain_output(noun[0], FALSE);
							write_text(temp_buffer);
							write_text(": ");
						}

						//printf("--- 1 calling functions for %s\n", object[noun[0]]->label);
						call_functions(pointer->word);

						/* IF INTERRUPTED BY SOME SPECIAL CONDITION, DON'T
						 * PERFORM THIS COMMAND FOR THE REMAINING OBJECTS */
						if (INTERRUPTED->value) {
							break;
						}
					}
				}
			} else {
				// FIRST LIST ISN'T MULTI
				//printf("--- first list isn't multi\n");
				if (list_size[1] > 1) {
					//printf("--- second list is multi\n");
					// ONLY SECOND IS MULTI
					noun[0] = first_available(0);

					for (index = 0; index < max_size[1]; index++) {
						/* CALL ALL THE FUNCTIONS ONCE FOR EACH OJECT */
						if (object_list[1][index] != 0) {
							noun[1] = object_list[1][index];

							if (MULTI_PREFIX->value) {
								// DISPLAY THE OBJECT BEING ACTED ON
								plain_output(noun[0], FALSE);
								write_text(temp_buffer);
								write_text(": ");
							}

							//printf("--- 2 calling functions for %s\n", object[noun[1]]->label);
							call_functions(pointer->word);

							/* IF INTERRUPTED BY SOME SPECIAL CONDITION, DON'T
							 * PERFORM THIS COMMAND FOR THE REMAINING OBJECTS */
							if (INTERRUPTED->value) {
								break;
							}
						}
					}
				} else {
					//printf("--- second list isn't multi\n");
					// NEITHER OBJECT REFERENCE IS MULTI
					if (list_size[0] == 0) {
						noun[0] = 0;
						noun[1] = 0;

						/* THIS IS AN OBJECT-LESS COMMAND */
						call_functions(pointer->word);
					} else {
						noun[0] = first_available(0);
						noun[1] = first_available(1);

						//printf("--- 3 calling functions for %s and %s\n", object[noun[0]]->label, object[noun[1]]->label);
						call_functions(pointer->word);
					}
				}
			}
			if (TIME->value == FALSE) {
				/* IS THIS ENOUGH TO INDICATE THAT THE OTHER COMMANDS
				 * SHOULDN'T BE PERFORMED??? */
				//INTERRUPTED->value = TRUE;
			}
			return;
		} else {
			//printf("--- move to next grammar sibling\n");
			// MOVE THROUGH THE OPTIONS AT THIS LEVEL OF THE GRAMMAR TREE
			// TO FIND THE ACTION THAT MATCHES THIS COMMAND
			if (pointer->next_sibling == NULL) {
				break;
			} else {
				pointer = pointer->next_sibling;
			}
		}
	} while (TRUE);

	/* THERE IS NO FUNCTION AS A CHILD OF THE LAST WORD OF THE PLAYER'S
	 * COMMAND SO DISPLAY AN IN-GAME ERROR TO THE PLAYER. THIS IS LIKELY
	 * TO BE AN INCOMPLETE COMMAND */
	INTERRUPTED->value = TRUE;
	diagnose();
	return;
}

/* THIS FUNCTION RETURNS THE FIRST OBJECT IN THE SPECIFIED LIST
 * OR 0 IF IT IS EMPTY. THE FIRST OBJECT IN THE LIST IS THE FIRST
 * ELEMENT THAT IS NON-ZERO BEFORE THE max-size IS REACHED. */
int first_available(int list_number) {
	int index;

	if (list_size[list_number] == 0) return (0);

	//printf("--- looking for next object in list\n");
	for (index = 0; index < max_size[list_number]; index++) {
		if (object_list[list_number][index] != 0) {
			//printf("--- returning object %s\n", object[object_list[list_number][index]]->label);
			return (object_list[list_number][index]);
		}
	}

	//printf("--- no objects left in list\n");
	/* NO OBJECTS LEFT IN THE LIST */
	return (0);
}

void call_functions(const char *base_name) {
	/* THIS FUNCTION CALLS ALL THE APPROPRIATE JACL FUNCTIONS TO RESPOND
	 * TO A PLAYER'S COMMAND GIVEN A BASE FUNCTION NAME AND THE CURRENT
	 * VALUE OF noun1 AND noun2 */

	/* THE DEFAULT IS THAT THE COMMAND IS SUCCESSFUL AND THAT TIME SHOULD
	 * PASS. IF THE COMMAND FAILS, 'TIME' WILL BE SET TO FALSE */
	TIME->value = TRUE;

	strncpy(base_function, base_name + 1, 80);
	strcat(base_function, "_");

	strncpy(override_, base_function, 80);

	strcpy(before_function, "+before_");
	strcat(before_function, base_name + 1);

	strcpy(after_function, "+after_");
	strcat(after_function, base_name + 1);

	strcpy(local_after_function, "after_");
	strcat(local_after_function, base_name + 1);
	if (noun[1] != FALSE) {
		strcat(local_after_function, "_");
		strcat(local_after_function, object[noun[1]]->label);
	}
	if (noun[0] != FALSE) {
		strcat(local_after_function, "_");
		strcat(local_after_function, object[noun[0]]->label);
	}

	/* THIS IS CALLED IF AN 'override' COMMAND IS EXECUTED
	 * IN A LIBRARY FUNCTION BUT THE OBJECT-OR-LOCATION-SPECIFIC
	 * OVERRIDE DOES NOT EXIST. IT IS SET TO '+default_func' */
	strcpy(default_function, "+default_");
	strcat(default_function, base_name + 1);

	/* EXECUTE THE GLOBAL *DEFAULT* BEFORE FUNCTION
	 * AND RETURN IF IT RETURNS TRUE */
	if (execute("+before") != FALSE)
		return;

	/* EXECUTE THE VERB-SPECIFIC BEFORE
	   FUNCTION AND RETURN IF IT RETURNS TRUE */
	if (execute(before_function) != FALSE)
		return;

	if (noun[0] == FALSE) { /* USER'S COMMAND HAS NO NOUNS */
		strcat(base_function, object[HERE]->label);
		/* EXECUTE THE FUNCTION 'func_here' */
		if (execute(base_function) == FALSE) {
			/* THIS LOCATION-SPECIFIC FUNCTION DOES NOT
			 * EXIST OR HAS ISSUED A 'break false' COMMAND.
			 * EXECUTE THE FUNCTION '+func'
			 * WITH THE POSSIBLILITY OF
			 * EXECUTING THE FUNCTION 'func_override_here'
			 * IF AN 'override' COMMAND IS FOUND IN '+func'
			 * IF THIS OVERRIDE FUNCTION ISN'T FOUND
			 * THE DEFAULT FUNCTION WILL BE EXECUTED */

			/* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT
			 * IS NEEDED */
			strcat(override_, "override_");
			strcat(override_, object[HERE]->label);

			/* CREATE THE FUNCTION NAME '+func' */
			strcpy(base_function, "+");
			strcat(base_function, base_name + 1);

			/* CALL THE LIBRARY'S DEFAULT BEHAVIOR */
			if (execute(base_function) == FALSE)
				unkfunrun(base_function);
		}
	} else if (noun[1] == FALSE) { /* USER'S COMMAND HAS ONE NOUN */
		strcat(base_function, object[noun[0]]->label);
		/* EXECUTE THE FUNCTION 'func_noun1' */
		if (execute(base_function) == FALSE) {
			/* THIS OBJECT-SPECIFIC FUNCTION DOES NOT
			 * EXIST OR HAS ISSUED A 'break false' COMMAND.
			 * EXECUTE THE FUNCTION '+func'
			 * WITH THE POSSIBLILITY OF
			 * EXECUTING THE FUNCTION 'func_override_noun1'
			 * IF AN 'override' COMMAND IS FOUND IN '+func'
			 * IF THIS OVERRIDE FUNCTION ISN'T FOUND
			 * THE DEFAULT FUNCTION WILL BE EXECUTED */

			/* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT
			 * IS NEEDED */
			strcat(override_, "override_");
			strcat(override_, object[noun[0]]->label);

			/* CREATE THE FUNCTION NAME '+func' */
			strcpy(base_function, "+");
			strcat(base_function, base_name + 1);

			/* CALL THE LIBRARY'S DEFAULT BEHAVIOR */
			if (execute(base_function) == FALSE)
				unkfunrun(base_function);
		}
	} else { /* USER'S COMMAND HAS TWO NOUNS */
		strcat(base_function, object[noun[1]]->label);
		strcat(base_function, "_");
		strcat(base_function, object[noun[0]]->label);
		/* EXECUTE THE FUNCTION 'func_noun2_noun1'
		 * IE give_to_fred THAT IS ASSOCIATED WITH
		 * THE OBJECT flint_stone */
		if (execute(base_function) == FALSE) {
			/* THIS OBJECTS-SPECIFIC FUNCTION DOES NOT
			 * EXIST OR HAS ISSUED A 'break false' COMMAND.
			 * EXECUTE THE FUNCTION '+func'
			 * WITH THE POSSIBLILITY OF
			 * EXECUTING THE FUNCTION 'func_override_noun2_noun1'
			 * IF AN 'override' COMMAND IS FOUND IN '+func'
			 * IF THIS OVERRIDE FUNCTION ISN'T FOUND
			 * THE DEFAULT FUNCTION WILL BE EXECUTED */

			/* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT
			 * IS NEEDED */
			strcat(override_, object[noun[1]]->label);
			strcat(override_, "_override_");
			strcat(override_, object[noun[0]]->label);

			/* CREATE THE FUNCTION NAME '+func' */
			strcpy(base_function, "+");
			strcat(base_function, base_name + 1);

			/* CALL THE LIBRARY'S DEFAULT BEHAVIOR */
			if (execute(base_function) == FALSE)
				unkfunrun(base_function);
		}
	}

	/* EXECUTE THE LOCAL AFTER FUNCTION
	 * AND RETURN IF IT RETURNS TRUE */
	if (execute(local_after_function) != FALSE)
		return;

	/* EXECUTE THE VERB-SPECIFIC AFTER
	 * FUNCTION AND RETURN IF IT RETURNS TRUE */
	if (execute(after_function) != FALSE)
		return;

	/* EXECUTE THE GLOBAL *DEFAULT* AFTER FUNCTION
	 * AND RETURN IF IT RETURNS TRUE */
	if (execute("+after") != FALSE)
		return;


	//sprintf(temp_buffer, "TIME = %d", TIME->value);
	//log_error(temp_buffer, PLUS_STDERR);

	if (TIME->value) {
		//printf("--- %s\n", base_function);
		eachturn();
	}

	return;
}

struct word_type *object_match(struct word_type *iterator, int noun_number) {
	/* THIS FUNCTION LOOPS THROUGH ALL THE POSIBILITIES IN THE CURRENT LEVEL
	 * OF THE GRAMMAR TREE TO SEE IF THERE ARE ANY OBJECT PLACE HOLDERS */

	/* STORE WHETHER BY THE END AN OBJECT PLACEHOLDER WAS ENCOUNTERED AND
	 * DISPLAY A MESSAGE IF NO OBJECTS MATCHED */
	int object_was_option = FALSE;

	do {
		/* THIS LOOP MEANS THAT CERTAIN ERRORS SUCH AS TAKING FROM A
		 * CLOSED CONTAINER CAN OCCUR MORE THAN ONCE */
		if ((iterator->word[0] == '*')) {
			object_was_option = TRUE;
			if (build_object_list(iterator, noun_number)) {
				/* RETURN THE POINT IN THE GRAMMAR TREE THAT MATCHED TO
				* CONTINUE ON FROM */
				//printf("--- returned TRUE from build_object_list\n");
				return (iterator);
			}
		}

		if (custom_error == TRUE) {
			/* AN ERROR OCCURED IN THE FIRST OBJECT PLACEHOLDER, DON'T
			 * TRY ANY OTHERS */
			return (NULL);
		}
	} while ((iterator = iterator->next_sibling) != NULL);

	/* THERE WERE NO OBJECT PLACE HOLDERS OR, IF THERE WERE, NO
	 * MATCHING OBJECTS COULD BE RESOLVED */
	//printf("--- returning null from object_match\n");

	if (object_was_option) {
		/* NO OBJECT MATCHED, BUT AN OBJECT WAS VALID AT THIS POINT IN THE
		 * PLAYER'S COMMAND */
		diagnose();
		custom_error = TRUE;
	}

	return (NULL);
}

struct word_type *exact_match(struct word_type *pointer) {
	/* THIS FUNCTION LOOPS THROUGH ALL THE POSIBILITIES IN THE CURRENT LEVEL
	 * OF THE GRAMMAR TREE TO SEE IF THERE ARE ANY EXACT MATCHES WITH THE
	 * CURRENT WORD IN THE PLAYER'S COMMAND.
	 * AN EXACT MATCH IS ANYTHING THAT ISN'T AN OBJECT PLACE HOLDER. */
	struct word_type *iterator = pointer;

	do {
		if (iterator->word[0] == '*') {
			/* THIS IS AN OBJECT PLACE HOLDER, THEREFORE IGNORE AND
			 * KEEP LOOKING FOR EXACT MATCHES */
		} else if (!strcmp(iterator->word, "$string")) {
			add_cstring("$string", word[wp]);
			last_exact = wp;
			wp++;
			return (iterator);
		} else if (!strcmp(iterator->word, "$integer") &&
		           validate(word[wp])) {
			add_cinteger("$integer", atoi(word[wp]));
			last_exact = wp;
			wp++;
			return (iterator);
		} else if (!strcmp(word[wp], iterator->word)) {
			last_exact = wp;
			wp++;
			return (iterator);
		}
	} while ((iterator = iterator->next_sibling) != NULL);

	/* THERE WERE NO EXACT MATCHES, SO RETURN FALSE */
	return (NULL);
}

int is_terminator(struct word_type *scope_word) {
	struct word_type *terminator = scope_word->first_child;

	if (terminator != NULL) {
		/* THERE MAY NO BE ANY MORE POSSIBLE WORDS IN THIS COMMAND
		 * BUT THERE SHOULD ALWAYS AT LEAST BE A BASE FUNCTION NAME */
		do {
			/* LOOP THROUGH ALL WORDS IN THE NEXT LEVEL OF THE
			 * GRAMMAR TABLE. THESE ARE THE WORDS THAT MARK THE END
			 * OF THE OBJECT REFERENCE PART OF THE COMMAND */
			if (!strcmp(word[wp], terminator->word)
			        || (!strcmp(terminator->word, "$integer")
			            && validate(word[wp]))) {
				return (TRUE);
			}
		} while ((terminator = terminator->next_sibling) != NULL);
	}

	return (FALSE);
}

int build_object_list(struct word_type *scope_word, int noun_number) {
	/* THIS FUNCTION BUILDS A LIST OF OBJECTS FROM THE PLAYER'S COMMAND
	 * AND RETURNS THE NUMBER OF OBJECTS IN THAT LIST */

	int             index, counter;
	int             resolved_object;
	const char      *except_word;

	//printf("--- entering build object list starting at %s with a scope_word of %s\n", word[wp], scope_word->word);
	/* LOOK AHEAD FOR A FROM CLAUSE AND STORE from_object IF SO */
	if (get_from_object(scope_word, noun_number) == FALSE) {
		/* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN
		 * DISPLAYED */
		//printf("--- from had an error\n");
		return (FALSE);
	}

	while (word[wp] != NULL) {
		/* LOOP THROUGH WORDS IN THE PLAYER'S INPUT ENDING WHEN EITHER
		 * THERE ARE NO MORE WORDS OR ONE OF THE CHILD NODES OF THE
		 * CURRENT scope_word NODE IS REACHED INDICATING THERE ARE NO
		 * MORE OBJECTS TO RESOLVE */

		if (!strcmp(word[wp], cstring_resolve("BUT_WORD")->value) ||
		        !strcmp(word[wp], cstring_resolve("EXCEPT_WORD")->value)) {
			/* START ADDING ALL FUTURE RESOLVED OBJECTS TO A SECOND LIST
			 * TO REMOVE FROM THE FIRST */
			except_word = word[wp];

			wp++;

			if (word[wp] != NULL && !strcmp(word[wp], cstring_resolve("FOR_WORD")->value)) {
				/* SKIP PAST THE WORD 'FOR' */
				wp++;
			}

			/* LOOK FORWARD FOR A FROM CLAUSE */
			if (get_from_object(scope_word, noun_number) == FALSE) {
				/* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN
				 * DISPLAYED */
				return (FALSE);
			}

			/* MOVE TO THE SECOND LIST THAT WILL ULTIMATELY BE SUBTRACTED
			 * FROM THE FIRST LIST */
			if (noun_number < 2) {
				/* CREATE A 'them' SET THAT CAN BE REFERRED TO IN THE
				 * 'except' CLAUSE */
				set_them(noun_number);
				/* JUMP TO THE 'EXCEPT' LIST THAT CORRESPONDS TO THIS
				 * RESOLVED LIST */
				noun_number = noun_number + 2;
			} else {
				sprintf(error_buffer, cstring_resolve("DOUBLE_EXCEPT")->value, except_word);
				write_text(error_buffer);
				custom_error = TRUE;
				return (FALSE);
			}
		} else if (after_from != -1 && !strcmp(word[wp], cstring_resolve("FROM_WORD")->value)) {
			/* SET THE WORD POINTER TO AFTER THE ALREADY-PROCESSED FROM
			 * CLAUSE (IF ONE EXISTED) AND CONTINUE */
			wp = after_from;
			//printf("--- hit from in processing moving on to word '%s'\n", word[wp]);

			/* IF NO OBJECTS WERE MATCHED BY THE TIME WE HIT FROM THEN EITHER
			 * PRESUME THE PLAYER MEANT 'ALL FROM' OR PRINT AN ERROR */
			if (list_size[noun_number] == 0) {
				//printf("--- adding all due to empty list.\n");
				add_all(scope_word, noun_number);
			}

			/* LOOK FOR THE NEXT FROM CLAUSE */
			if (get_from_object(scope_word, noun_number) == FALSE) {
				/* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN
				 * DISPLAYED */
				return (FALSE);
			}
		} else if (!strcmp("then", word[wp])) {
			break;
		} else if (is_terminator(scope_word)) {
			/* THERE ARE NO MORE OBJECTS TO RESOLVE */

			//printf("--- %s is a build list terminator\n", word[wp]);
			break;
		} else if (!strcmp(word[wp], "comma") ||
		           !strcmp(word[wp], cstring_resolve("AND_WORD")->value)) {
			/* JUST MOVE ONTO THE NEXT WORD AND SEE WHAT COME NEXT */
			wp++;
		} else {
			/* CALL noun_resolve TO FETCH THE FIRST MATCHING OBJECT */
			/* FALSE INDICATES THAT WE ARE NOT LOOKING FOR A FROM OBJECT */
			resolved_object = noun_resolve(scope_word, FALSE, noun_number);

			if (resolved_object == -1) {
				/* THERE WERE MULTIPLE MATCHES DUE TO PLURAL NAME
				 * BEING USED */
				index = 0;

				while (multiple_resolved[index] != 0) {
					/* ADD ALL THE RESOLVED OBJECTS TO THE LIST */
					add_to_list(noun_number, multiple_resolved[index]);
					index++;
				}
			} else if (resolved_object) {
				/* ADD IT TO THE LIST */
				add_to_list(noun_number, resolved_object);
			} else {
				/* NO OBJECTS COULD BE RESOLVED, THIS MIGHT NOT BE A BAD
				 * THING YET IF THERE ARE OTHER OBJECT PLACEHOLDERS TO
				 * COME THAT ARE LESS RESTRICTIVE */
				return (FALSE);
			}
		}
	}

	if (noun_number > 1 && list_size[noun_number] != 0) {
		/* A SECOND EXCEPTION LIST EXISTS, SUBTRACT IT FROM THE FIRST */
		//printf("--- there are some exceptions.\n");

		//printf("--- first list: %d, second list: %d\n", max_size[FIRST_LIST], max_size[SECOND_LIST]);
		/* LOOP THROUGH ALL THE ITEMS IN THE SECOND LIST */
		for (index = 0; index < max_size[SECOND_LIST]; index++) {
			if (object_list[SECOND_LIST][index] != 0) {
				/* THIS OBJECT IS A REAL OBJECT SO LOOP THROUGH ALL THE ITEMS
				 * IN THE FIRST LIST */
				//printf("--- exception object is %s\n", object[object_list[SECOND_LIST][index]]->label);
				for (counter = 0; counter < max_size[FIRST_LIST]; counter++) {
					/* LOOP THROUGH ALL THE OBJECTS IN THE FIRST LIST
					 * IF AN OBJECT FROM THE SECOND LIST EXISTS IN THE FIRST
					 * LIST, REMOVE IT */
					//printf("--- comparing %s = %s\n", object[object_list[FIRST_LIST][counter]]->label, object[object_list[SECOND_LIST][index]]->label);
					if (object_list[FIRST_LIST][counter] ==
					        object_list[SECOND_LIST][index]) {

						//printf("--- removing object %s\n", object[object_list[FIRST_LIST][counter]]->label);
						object_list[FIRST_LIST][counter] = 0;
						list_size[FIRST_LIST]--;
					}
				}
			}
		}
	}

	if (noun_number > 1) {
		/* IF THERE WERE EXCEPTIONS, MOVE BACK TO THE FIRST LIST */
		noun_number = FIRST_LIST;
	}

	/* THE RETURN TRUE IF AN OBJECT COULD BE RESOLVED */
	if (list_size[noun_number] != 0) {
		/* SET THEM ARRAY */
		set_them(noun_number);

		return (TRUE);
	} else {
		/* THE LIST IS NOW EMPTY, DISPLAY ' I DON'T SEE WHAT YOU
		 * ARE REFERRING TO.' ERROR */
		if (!strcmp(scope_word->word, "*held") ||
		        !strcmp(scope_word->word, "**held")) {
			write_text(cstring_resolve("NONE_HELD")->value);
		} else {
			write_text(cstring_resolve("NO_OBJECTS")->value);
		}

		custom_error = TRUE;
		return (FALSE);
	}
}

void set_them(int noun_number) {
	int index, counter;

	if (list_size[noun_number] == 1) {
		/* THERE IS ONLY ONE OBJECT LEFT IN THE LIST, FIND IT AND ADD
		 * TO THEM IF REQUIRED */
		for (index = 0; index < max_size[noun_number]; index++) {
			if (object_list[noun_number][index] != 0) {
				if (object[object_list[noun_number][index]]->attributes & PLURAL) {
					them[0] = object_list[noun_number][index];
					them[1] = 0;
					break;
				}
			}
		}
	} else {
		/* THERE IS MORE THAN ONE OBJECT IN THE LIST SO COPY IT TO THE
		 * 'THEM' LIST */
		counter = 0;

		for (index = 0; index < max_size[noun_number]; index++) {
			if (object_list[noun_number][index] != 0) {
				them[counter] = object_list[noun_number][index];
				//printf("--- storing %s in them list\n", object[them[counter]]->label);
				counter++;
			}
		}

		/* NULL TERMINATE THE LIST */
		them[counter] = 0;
	}
}

void add_all(struct word_type *scope_word, int noun_number) {
	int index;

	//printf("--- trying to add all\n");

	for (index = 1; index <= objects; index++) {
		if ((object[index]->MASS < HEAVY) &&
		        !(object[index]->attributes & LOCATION)) {
			if (is_direct_child_of_from(index) &&
			        scope(index, scope_word->word, RESTRICT)) {
				//printf("--- objects parent is %s\n", object[object[index]->PARENT]->label);
				add_to_list(noun_number, index);
			}
		}
	}
}

int is_child_of_from(int child) {
	/* THIS FUNCTION DETERMINES IF THE PASSED OBJECT IS A CHILD OF ANY OF
	 * THE RESOLVED 'FROM' OBJECTS, OR ANY OBJECT IN A FROM OBJECT */
	int index = 0;

	if (from_objects[0] == 0) {
		/* THERE HAS BEEN NO FROM CLAUSE */
		return (TRUE);
	}

	while (from_objects[index] != 0) {
		//printf("--- in is_child from object is %s, child is %s\n", object[from_objects[index]]->label, object[child]->label);
		/* THIS OLD WAY OF DOING THINGS ALLOWS SPECIFIC 'take thing from box'
		 * WHEN thing IS INSIDE SOMETHING ELSE INSIDE box. THAT IS KINDA COOL
		 * BUT NOT PARTICULARLY NECESSARY. BY ONLY CHECKING IMMEDIATE CHILDREN
		 * THE NON-RESTRICTIVE VERSION OF parent_of CAN BE USED FROM SCOPE
		 * WHEN SAYING take all from box BECAUSE THIS FROM STOPS OBJECT IN
		 * OBJECTS FROM BEING TAKING FROM SOMETHING, SO scope DOESN'T HAVE
		 * TO. THIS BEHAVIOUR WOULD NOT BE NECESSARY WHEN NOT taking all from */

		//if (parent_of(from_objects[index], child, RESTRICT)) {
		if (object[child]->PARENT == from_objects[index]) {
			//printf("--- %s is in %s\n", object[child]->label, object[from_objects[index]]->label);
			return (TRUE);
		}
		index++;
	}

	return (FALSE);
}

int is_direct_child_of_from(int child) {
	/* THIS FUNCTION DETERMINES IF THE PASSED OBJECT IS A CHILD OF ANY OF
	 * THE RESOLVED 'FROM' OBJECTS, OR ANY OBJECT IN A FROM OBJECT */
	int index = 0;

	if (from_objects[0] == 0) {
		/* THERE HAS BEEN NO FROM CLAUSE */
		return (TRUE);
	}

	while (from_objects[index] != 0) {
		//printf("--- in is_direct from object is %s\n", object[from_objects[index]]->label);
		if (object[child]->PARENT == from_objects[index]) {
			//printf("--- object %s is in the from object\n", object[child]->label);
			return (TRUE);
		}
		index++;
	}

	return (FALSE);
}

int get_from_object(struct word_type *scope_word, int noun_number) {
	/* THIS FUNCTION LOOKS AHEAD TO FIND IF THE CURRENT OBJECT REFERENCE
	 * IS QUALIFIED BY A 'FROM' WORD. IT RETURNS FALSE ON AN ERROR
	 * CONDITION AND TRUE OTHERWISE, REGARDLESS OF WHETHER A FROM OBJECT
	 * IS SPECIFIED */

	int index, counter, from_object;

	/* TAKE A COPY OF THE CURRENT VALUE OF wp */
	int backup = wp;

	/* SET TERMINATOR TO THE FIRST OF THE TERMINATING WORDS */
	struct word_type *terminator = scope_word->first_child;

	/* SEE IF 'FROM' IS ONE OF THE TERMINATORS OF THIS CURRENT OBJECT
	 * PLACEHOLDER. IF SO, DON'T LOOK FOR A FROM OBJECT */
	if (terminator != NULL) {
		//printf("--- checking if terminator word (%s) is from\n", terminator->word);
		if (!strcmp(cstring_resolve("FROM_WORD")->value, terminator->word)) {
			//printf("--- from is a terminator, don't get a from object\n");
			return (TRUE);
		}
		while ((terminator = terminator->next_sibling) != NULL);
	}

	/* LOOP FROM THE CURRENT WORD TO THE NEXT TERMINATOR AND LOOK FOR THE
	 * WORD 'FROM' AND STORE THE FOLLOWING OBJECT */
	while (word[wp] != NULL) {
		//printf("--- from loop checking %s\n", word[wp]);
		if (!strcmp(word[wp], cstring_resolve("FROM_WORD")->value)) {
			from_word = word[wp];
			wp++;

			/* scope_word FOR THE CURRENT OBJECT IS PASSED ONLY SO
			 * noun_resolve CAN FIND OUT THE APPROPRIATE TERMINATORS
			 * THE ACCEPTABLE SCOPE FOR THE FROM OBJECT SHOULD BE
			 * AT LEAST *present */
			/* TRUE INDICATES THAT WE ARE LOOKING FOR A FROM OBJECT */
			from_object = noun_resolve(scope_word, TRUE, noun_number);

			/* STORE THE wp FROM AFTER THE RESOLVED FROM OBJECT SO
			 * WE CAN JUMP FORWARD TO HERE AGAIN WHEN WE HIT THIS
			 * FROM CLAUSE IN PROCESSING (THIS FUNCTION IS A LOOK-AHEAD */
			after_from = wp;

			//printf("--- looked forward and found a from object of %s\n", object[from_object]->label);

			if (from_object == -1) {
				/* THERE WERE MULTIPLE MATCHES DUE TO PLURAL NAME
				 * BEING USED */
				index = 0;
				counter = 0;
				/* LOOK THROUGH ALL THE OBJECTS RESOLVED AND CHECK THAT
				 * THEY ARE ALL VALID FROM OBJECTS */
				while (multiple_resolved[index] != 0) {
					if (verify_from_object(multiple_resolved[index]) == FALSE) {
						/* AS SOON AS ONE IS BAD, ABORT THE FROM CLAUSE */
						return (FALSE);
					} else {
						from_objects[counter] = multiple_resolved[index];
						counter++;
					}
					index++;
				}

				/* NULL TERMINATE THE LIST */
				from_objects[counter] = 0;

				/* OBJECTS HAVE BEEN SET AND ARE VALID, RESTORE THE WORD
				 * POINTER */
				wp = backup;
				return (TRUE);
			} else if (from_object) {
				if (verify_from_object(from_object) == FALSE) {
					return (FALSE);
				} else {
					/* ADD THIS OBJECT TO THE NULL TERMINATED LIST */
					from_objects[0] = from_object;
					from_objects[1] = 0;

					/* OBJECT HAS BEEN SET AND IS VALID, RESTORE THE WORD
					* POINTER */
					wp = backup;
					return (TRUE);
				}
			} else {
				/* FROM UNKNOWN OBJECT, DISPLAY ERROR */
				diagnose();
				custom_error = TRUE;
				return (FALSE);
			}
			//printf("--- finished processing from clause, next word is %s\n", word[wp]);
		} else if (!strcmp(cstring_resolve("EXCEPT_WORD")->value, word[wp]) ||
		           !strcmp(cstring_resolve("BUT_WORD")->value, word[wp])) {
			/* THIS IS THE LIMIT OF THE EFFECT ANY FROM OBJECT ANYWAY,
			 * SO TREAT IT LIKE A TERMINATOR */
			//printf("--- %s is a get_from_object except word\n", word[wp]);
			wp = backup;
			return (TRUE);
		} else if (is_terminator(scope_word) || !strcmp("then", word[wp])) {
			/* THERE ARE NO MORE OBJECTS TO RESOLVE */

			//printf("--- %s is a get_from_object terminator\n", word[wp]);
			wp = backup;
			return (TRUE);
		}
		wp++;
	}

	/* HIT THE END OF THE PLAYER'S COMMAND BEFORE A 'FROM' WORD */
	//printf("--- no from clause specified in this block\n");
	wp = backup;
	return (TRUE);
}

int verify_from_object(int from_object) {
	//printf("--- from object is %s\n", object[from_object]->label);
	//if (!(object[from_object]->attributes & CONTAINER) &&
	//  !(object[from_object]->attributes & SURFACE) &&
	//  !(object[from_object]->attributes & ANIMATE)) {
	//  sprintf (error_buffer, FROM_NON_CONTAINER, from_word);
	//  write_text (error_buffer);
	//  custom_error = TRUE;
	//  return (FALSE);
	//} else if (object[from_object]->attributes & CLOSED) {
	if (object[from_object]->attributes & CONTAINER && object[from_object]->attributes & CLOSED) {
		//printf("--- container is concealing\n");
		if (object[from_object]->attributes & FEMALE) {
			sprintf(error_buffer, cstring_resolve("CONTAINER_CLOSED_FEM")->value, sentence_output(from_object, TRUE));
		} else {
			sprintf(error_buffer, cstring_resolve("CONTAINER_CLOSED")->value, sentence_output(from_object, TRUE));
		}
		write_text(error_buffer);
		custom_error = TRUE;
		return (FALSE);
		/* IF THE PERSON IS CONCEALING, THEN THE OBJECT CAN'T BE REFERRED TO
		 * IF THE PERSON IS POSSESSIVE LET THE LIBRARY HANDLE THE RESPONSE
		} else if (object[from_object]->attributes & POSSESSIVE) {
		    //printf("--- container is closed\n");
		    sprintf (error_buffer, PERSON_POSSESSIVE, sentence_output(from_object, TRUE));
		    write_text(error_buffer);
		    custom_error = TRUE;
		    return (FALSE);
		} else if (object[from_object]->attributes & CONCEALING) {
		    //printf("--- container is closed\n");
		    sprintf (error_buffer, PERSON_CONCEALING, sentence_output(from_object, TRUE));
		    write_text(error_buffer);
		    custom_error = TRUE;
		    return (FALSE);*/
	}

	//printf("--- set from object just fine\n");
	return (TRUE);
}

void add_to_list(int noun_number, int resolved_object) {
	/* ADD THIS OBJECT TO THE OBJECT LIST DEPENDING */
	/* AND SET IT, THEM, HER AND HIM */
	if (!(object[resolved_object]->attributes & ANIMATE))
		it = resolved_object;
	if (object[resolved_object]->attributes & ANIMATE
	        && object[resolved_object]->attributes & FEMALE)
		her = resolved_object;
	if (object[resolved_object]->attributes & ANIMATE
	        && !(object[resolved_object]->attributes & FEMALE))
		him = resolved_object;

	//printf("--- adding_object %s to list %d at index %d\n", object[resolved_object]->label, noun_number, max_size[noun_number]);
	object_list[noun_number][max_size[noun_number]] = resolved_object;
	list_size[noun_number]++;
	max_size[noun_number]++;
}

int noun_resolve(struct word_type *scope_word, int finding_from, int noun_number) {
	/* THIS FUNCTION STARTS LOOKING AT THE PLAYER'S COMMAND FROM wp ONWARDS
	 * AND LOOKS FOR OBJECTS IN THE SCOPE SPECIFIED BY THE GRAMMAR ELEMENT
	 * POINTED TO BY THE PASSED pointer */

	/* THIS IS SET TO TRUE WHEN THE CURRENT WORD MATCHES THE CURRENT OBJECT */
	int             object_matched;

	/* THIS IS SET TO > 0 WHEN THE PLURAL FORM OF AN OBJECT IS USED */
	int             return_limit = 0;

	int             index;
	int             counter;
	int             first_word = TRUE;

	struct word_type *terminator = scope_word->first_child;
	struct name_type *current_name;

	matches = 0;
	highest_confidence = 0;
	prime_suspect = 0;
	done = FALSE;
	backup_pointer = wp;
	everything = FALSE;

	if (word[wp] == NULL) {
		/* NOTHING TO RESOLVE... */
		return (FALSE);
	}

	/* SET THE CONFIDENCE FOR EACH OBJECT TO 1 TO INDICATE THAT THEY ARE
	 * ALL EQUALLY POSSIBLE AT THE MOMENT. SET TO ZERO TO DISCOUNT */
	for (index = 1; index <= objects; index++)
		confidence[index] = 1;

	/* CLEAR THE OBJECT NAME BEFORE BUILDING IT WAS WE GO ALONG */
	object_name[0] = 0;

	/* CHECK FOR A QUANTITY QUALIFIER */
	if (validate(word[wp])) {
		/* FIRST WORD IS AN INTEGER AND SECOND WORD IS 'OF' SO
		 * TREAT THIS AS A LIMIT QUALIFIER BEFORE STARTING TO
		 * PROCESS THE REST OF THE WORDS */
		if (word[wp + 1] != NULL && !strcmp(word[wp + 1], cstring_resolve("OF_WORD")->value)) {
			return_limit = atoi(word[wp]);

			/* MAKE SURE THE RETURN LIMIT IS SOMETHING SENSIBLE */
			if (return_limit < 1) {
				return_limit = 1;
			}

			object_expected = TRUE;
			strcpy(object_name, word[wp]);
			strcat(object_name, " ");
			strcat(object_name, cstring_resolve("OF_WORD")->value);

			/* MOVE THE WORD POINTER TO AFTER THE 'OF' */
			wp = wp + 2;
		}
		/* IF AN INTEGER IS NOT FOLLOWED BY 'OF', PRESUME IT IS AN OBJECT
		 * NAME */
	}

	// CLEAR THE ERROR BUFFER AND USE IT TO STORE ALL THE WORDS THE PLAYER
	// HAS USED TO REFER TO THE OBJECT
	error_buffer[0] = 0;

	while (word[wp] != NULL) {
		// ADD THE WORDS USED TO error_buffer FOR POSSIBLE USE
		// IN A DISABMIGUATE EMESSAGE
		if (first_word == FALSE) {
			strcat(error_buffer, " ");
		}
		strcat(error_buffer, word[wp]);
		first_word = FALSE;

		/* LOOP THROUGH WORDS IN THE PLAYER'S INPUT */

		/* RESET TERMINATOR TO THE FIRST OF THE TERMINATING WORDS */
		terminator = scope_word->first_child;

		if (terminator != NULL) {
			/* THERE MAY NO BE ANY MORE POSSIBLE WORDS IN THIS COMMAND
			 * BUT THERE SHOULD ALWAYS AT LEAST BE A BASE FUNCTION NAME */
			do {
				/* LOOP THROUGH ALL WORDS IN THE NEXT LEVEL OF THE
				 * GRAMMAR TABLE. THESE ARE THE WORDS THAT MARK THE END
				 * OF THE OBJECT REFERENCE PART OF THE COMMAND */
				//printf("--- checking terminator word %s\n", terminator->word);
				if (!strcmp(word[wp], terminator->word)
				        || (!strcmp(word[wp], cstring_resolve("FROM_WORD")->value))
				        || (!strcmp(word[wp], cstring_resolve("AND_WORD")->value))
				        || (!strcmp(word[wp], "comma"))
				        || (!strcmp(word[wp], cstring_resolve("BUT_WORD")->value))
				        || (!strcmp(word[wp], cstring_resolve("THEN_WORD")->value))
				        || (!strcmp(word[wp], cstring_resolve("EXCEPT_WORD")->value))
				        || (!strcmp(terminator->word, "$integer")
				            && validate(word[wp]))) {
					if (!matches) {
						/* A TERMINATOR HAS BEEN FOUND BEFORE A
						 * SINGLE MATCHING OBJECT NAME. */
						return (FALSE);
					} else {
						/* A TERMINATOR HAS BEEN FOUND, BUT NOT
						 * BEFORE MATCHING OBJECT NAMES. JUMP FORWARD TO
						 * RESOLVING INTO OBJECT NUMBER(S) */
						done = TRUE;
						break;
					}
				}
			} while ((terminator = terminator->next_sibling) != NULL);
		}

		if (done == TRUE) {
			/* A TERMINATING WORD HAS BEEN REACHED, SO DON'T TEST THIS
			 * WORD AGAINST THE OBJECT NAMES, JUST MOVE ON TO CHOSING
			 * AN OBJECT. */
			//printf("--- %s is a terminator\n", word[wp]);
			break;
		}

		//puts("--- passed checking for a terminator");

		/* ADD THE CURRENT WORD TO THE NAME OF THE OBJECT THE PLAYER
		 * IS TRYING TO REFER TO FOR USE IN AN ERROR MESSAGE IF
		 * LATER REQUIRED */
		if (object_name[0] != 0)
			strcat(object_name, " ");

		strcat(object_name, word[wp]);

		if (!strcmp("everything", word[wp])) {
			/* ALL THIS NEEDS TO SIGNIFY IS THAT IT IS OKAY TO RETURN MULTIPLE
			 * RESULTS. EVERYTHING ELSE SHOULD TAKE CARE OF ITSELF GIVEN ALL
			 * OBJECTS START OF WITH A CONFIDENCE OF 1, AND ALL OBJECTS WITH
			 * A CONFIDENCE > 0 ARE RETURNED WHEN return_limit > 1 */
			if (return_limit == FALSE) {
				return_limit = MAX_OBJECTS;
			}

			/* THIS IS USED TO ALTER THE BEHAVIOUR OF SCOPE SELECTION LATER */
			everything = TRUE;

			matches = 0;

			//printf("--- entering for loop\n");
			for (index = 1; index <= objects; index++) {
				if (confidence[index] != FALSE) {
					matches++;
				}
			}
			//printf("--- exiting for loop\n");

			if (word[wp + 1] != NULL && !strcmp(cstring_resolve("OF_WORD")->value, word[wp + 1])) {
				/* MOVE PAST THE 'OF' IF IT IS NEXT */
				wp++;
			}
			//printf("--- finished setting matches to %d for all\n", matches);
		} else {
			/* THE CURRENT WORD IS NOT ONE OF THE NEXT POSSIBLE WORDS IN THE
			 * PLAYER'S COMMAND OR ANY OF THE SPECIAL MEANING WORDS, THEREFORE
			 * TEST IT AGAINST ALL OF THE NAMES OF ALL OF THE OBJECTS. */
			for (index = 1; index <= objects; index++) {
				if (!confidence[index]) {
					/* SKIP OBJECTS THAT HAVE ALREADY
					 * BEEN EXCLUDED BY A PREVIOUS WORD */
					continue;
				}

				/* NEXT WORD IN PLAYERS INPUT IS YET TO
				 * BE TESTED AGAINST ALL THIS OBJECT'S NAMES */
				object_matched = FALSE;

				if (!strcmp(cstring_resolve("IT_WORD")->value, word[wp]) ||
				        !strcmp(cstring_resolve("ITSELF_WORD")->value, word[wp])) {
					if (it == FALSE) {
						no_it();
						return (FALSE);
					} else {
						if (index == it) {
							object_matched = TRUE;
						}
					}
				} else if (!strcmp(cstring_resolve("HER_WORD")->value, word[wp]) ||
				           !strcmp(cstring_resolve("HERSELF_WORD")->value, word[wp])) {
					if (her == FALSE) {
						no_it();
						return (FALSE);
					} else {
						if (index == her) {
							object_matched = TRUE;
						}
					}
				} else if (!strcmp(cstring_resolve("HIM_WORD")->value, word[wp]) ||
				           !strcmp(cstring_resolve("HIMSELF_WORD")->value, word[wp])) {
					if (him == FALSE) {
						no_it();
						return (FALSE);
					} else {
						if (index == him) {
							object_matched = TRUE;
						}
					}
				} else if (!strcmp(cstring_resolve("THEM_WORD")->value, word[wp]) ||
				           !strcmp(cstring_resolve("THEMSELVES_WORD")->value, word[wp]) ||
				           !strcmp(cstring_resolve("ONES_WORD")->value, word[wp])) {
					/* THIS NEED ONLY BE THE SIZE OF 'THEM', BUT NO HARM
					 * IN MAKING IT THE FULL SIZE */
					if (return_limit == FALSE) {
						return_limit = MAX_OBJECTS;
					}

					/* LOOP THROUGH ALL THE OBJECT IN THE 'THEM' ARRAY AND
					 * SEE IF THIS OBJECT IS PRESENT */
					counter = 0;

					while (them[counter] != 0) {
						if (them[counter] == index) {
							//printf("--- found previous them object %s\n", object[index]->label);
							object_matched = TRUE;
							break;
						}
						counter++;
					}
					/*} else if (!strcmp("1", word[wp])) {
					    return_limit = 1;

					    // LOOP THROUGH ALL THE OBJECT IN THE 'THEM' ARRAY AND
					    // SEE IF THIS OBJECT IS PRESENT
					    counter = 0;

					    while (them[counter] != 0) {
					        if (them[counter] == index) {
					            //printf("--- found previous them object %s\n", object[index]->label);
					            object_matched = TRUE;
					            break;
					        }
					        counter++;
					    }*/
				} else {
					current_name = object[index]->first_name;

					while (current_name != NULL) {
						/* LOOP THROUGH ALL THE CURRENT OBJECTS NAMES */
						if (!strcmp(word[wp], current_name->name)) {
							/* CURRENT WORD MATCHES THE CURRENT NAME
							 *OF THE CURRENT OBJECT */
							//printf("--- %s is a name match of object %d\n", word[wp], index);
							object_matched = TRUE;

							/* MOVE ON TO NEXT OBJECT, THIS OBJECT SHOULD
							 * NOT HAVE THE SAME NAME TWICE */
							break;
						}
						current_name = current_name->next_name;
					}

					/* NOW LOOP THROUGH ALL THE OJBECTS PLURAL NAMES */
					current_name = object[index]->first_plural;

					while (current_name != NULL) {
						/* LOOP THROUGH ALL THE CURRENT OBJECTS NAMES */
						if (!strcmp(word[wp], current_name->name)) {
							/* CURRENT WORD MATCHES THE CURRENT NAME
							 *OF THE CURRENT OBJECT */
							//printf("--- %s is a plural name match of object %d\n", word[wp], index);
							object_matched = TRUE;

							/* IT IS NOW OKAY FOR THIS FUNCTION TO RETURN MORE
							 * THAT ONE MATCHING OBJECT AS THE PLURAL FORM
							 * HAS BEEN USED. THIS IS INDICATED BY RETURNING
							 * -1 TO INDICATED THAT THE OBJECT LIST IS STORED
							 * IN A GLOBALLY ACCESSIBLE ARRAY */
							if (return_limit == FALSE) {
								return_limit = MAX_OBJECTS;
							}

							/* MOVE ON TO NEXT OBJECT, THIS OBJECT SHOULD
							 * NOT HAVE THE SAME NAME TWICE */
							break;
						}
						current_name = current_name->next_name;
					}
				}

				if (object_matched) {
					/* THE CURRENT WORD MATCHES ONE OF THE NAMES OF THE
					 * CURRENT OBJECT. */
					if (confidence[index] == 1) {
						/* OBJECT HAD NOT YET BEEN COUNTED AS MATCH SO INCREMENT
						 * THE NUMBER OF OBJECTS THAT MATCH SO FAR. */
						matches++;
					}
					if (confidence[index] != FALSE) {
						/* IF OBJECT HAS NOT BEEN EXCLUDED, INCREMENT THE
						 * NUMBER OF NAMES IT HAS MATCHING. */
						confidence[index]++;
					}
				} else {
					/* THE CURRENT WORD IS NOT ONE OF THE NAMES OF THE
					 * CURRENT OBJECT.
					 * IF THE OBJECT HAD PREVIOUSLY BEEN A CANDIDATE, DECREMENT
					 * THE NUMBER OF MATCHING OBJECTS. */
					if (confidence[index] > 1)
						matches--;

					/* AS THE CURRENT WORD DID NOT MATCH ANY OF THE NAMES OF
					 * THE THIS OBJECT, EXCLUDE IT FROM CONTENTION. */
					confidence[index] = FALSE;
				}
			} /* MOVE ON TO NEXT OBJECT FOR IN LOOP */
		}

		if (!matches) {
			/* IF THERE ARE NO REMAINING MATCHES DON'T MOVE ON TO THE NEXT
			 * WORD IN THE PLAYER'S INPUT. */
			//printf("--- %s isnt a name match for any object\n", word[wp]);

			/* THIS WORD IS A LIKELY BE INCORRECT AS IT DIDN'T MATCH
			 * ANY OBJECTS */
			if (oops_word == -1 && word[wp] != NULL) {
				oops_word = wp;
			}

			break;
		}

		/* MOVE ON TO THE NEXT WORD IN THE PLAYER'S INPUT. */
		//printf("--- moving on to next word\n");
		wp++;
	}

	//printf("--- finished processing words\n");

	/***************************************************
	 * FINISHED LOOKING THROUGH THE PLAYER'S INPUT NOW *
	 * MOVE THE POINTER BACK FOR PARSER TO PROCESS     *
	 * THIS SHOULDN'T BE DONE FOR A WORD LIKE 'AND'    *
	 ***************************************************/

	if (return_limit == FALSE) {
		/* THE RETURN LIMIT WAS NEVER SET, SO TREAT THIS AS AN IMPLICIT 1 */
		return_limit = 1;
	}

	if (matches == 0) {
		/* THE PLAYER'S INPUT COULD NOT BE RESOLVED TO A SINGLE OBJECT
		 * BUT NO TERMINATING WORD WAS USED BEFORE A NON-TERMINATING
		 * WORD IN A PLACE WHERE AN OBJECT COULD BE SPECIFIED ---
		 * IN OTHER WORDS, PRESUME THE PLAYER WAS TRYING TO REFER TO
		 * AN OBJECT BUT SOMEHOW GOT IT WRONG */

		//printf("--- matches = 0\n");
		/* THIS VARIABLE IS USED TO CHANGE THE BEHAVIOR OF diagnose()
		 * SHOULD THIS COMMAND TURN OUT TO BE INVALID */
		object_expected = TRUE;

		wp = backup_pointer;    /* BACK UP THE CURRENT WORD POINTER.
                                 * SO THE PARSER CAN INVESTIAGE THE
                                 * POSIBILITY THAT THIS ISN'T A
                                 * REFERENCE TO AN OBJECT AT ALL */

		return (FALSE);         /* RETURN TO PARSER WITH NO MATCHING
                                 * OBJECT HAVING BEEN FOUND. */
	}

	//printf("--- starting with %d matches\n", matches);

	/* LOOP THROUGH ALL OBJECTS AND REMOVE FROM ANY OBJECT THAT IS NOT IN THE
	 * CURRENT LOCATION, PROVIDED THE VERB DOESN'T ALLOW THE OBJECT TO
	 * BE *ANYWHERE. */
	for (index = 1; index <= objects; index++) {
		if (confidence[index] != FALSE && strcmp(scope_word->word, "*anywhere") && strcmp(scope_word->word, "**anywhere") && strcmp(scope_word->word, "*location")) {
			if (scope(index, "*present", UNRESTRICT) == FALSE) {
				matches--;
				confidence[index] = FALSE;
				//printf("--- removing %s for not being present\n", object[index]->label);
			}
		}

		/* IF THE OBJECT IS IMPLICITLY INCLUDED DUE TO A MULTIPLE RETURN
		 * LIMIT, REMOVE IT IF IT IS PART OF THE SCENERY */
		if (confidence[index] == 1 && return_limit > 1) {
			if ((object[index]->MASS >= HEAVY) ||
			        (object[index]->attributes & LOCATION)) {
				matches--;
				confidence[index] = 0;
				//printf("--- removing %s for being scenery\n", object[index]->label);
			}
		}

		/* FOR ALL CONTENDERS, CALCULATE THE CONFIDENCE AS A PERCENTAGE,
		 * SO LONG AS WE ARE STILL LOOKING FOR A SINGLE OBJECT */
		if (confidence[index] != FALSE && return_limit == 1) {
			current_name = object[index]->first_name;
			counter = 0;
			while (current_name != NULL) {
				counter++;
				current_name = current_name->next_name;
			}
			confidence[index] = ((confidence[index] - 1) * 100) / counter;
		}
	}

	// CALCULATE THE HIGHEST CONFIDENCE OF ALL THE POSSIBLE OBJECTS
	highest_confidence = 0;

	for (index = 1; index <= objects; index++) {
		if (confidence[index] > highest_confidence) {
			highest_confidence = confidence[index];
		}
	}

	/* REMOVE ANY OBJECTS THAT ARE NOT IN THEIR VERB'S SPECIFIED SCOPE */
	for (index = 1; index <= objects; index++) {
		if (confidence[index] != FALSE) {
			if (scope(index, "*present", UNRESTRICT) != FALSE) {
				// TAKE SPECIAL NOT OF AN OBJECT THAT IS PRESENT
				// AND AT LEAST EQUAL FOR THE HIGHEST CONFIDENCE
				// IN CASE NO OBJECT ARE LEFT AFTER SPECIFIED SCOPE
				// IS USED TO FILTER THE LIST */
				if (confidence[index] == highest_confidence) {
					prime_suspect = index;
				}
				//printf("--- storing %s as prime_suspect\n", object[index]->label);
			}

			if (finding_from) {
				if (strcmp(scope_word->word, "*anywhere") && strcmp(scope_word->word, "**anywhere")) {
					if (scope(index, "*present") == FALSE) {
						matches--;
						confidence[index] = FALSE;
						continue;
					}
				}
			} else if (scope(index, scope_word->word, (everything && !from_objects[0])) == FALSE) {
				/* IF everything IS TRUE, scope IS RESTRICTED */
				//printf("--- removing %s due to regular scope\n", object[index]->label);
				matches--;
				confidence[index] = FALSE;
				continue;
			}

			/* CHECK IF THIS OBJECT IS IN ACCORDANCE WITH ANY FROM CLAUSE
			 * THAT MAY OR MAY NOT HAVE USED */
			if (is_child_of_from(index) == FALSE) {
				matches--;
				confidence[index] = FALSE;
				//printf("--- removing %s due to from clause\n", object[index]->label);
			}
		}
	}

	//printf("--- there are %d matches left\n", matches);

	/* THIS LOOP REMOVES ANY OBJECT THAT ARE NOT EQUAL TO THE HIGHEST
	 * CONFIDENCE UNLESS A PLURAL NAME WAS USED. IN THAT CASE, ONLY
	 * EXCLUDE OBJECTS THAT DO NOT HAVE ONE OF THE NAMES SUPPLIED */
	if (matches > 1 && return_limit == 1) {
		// CALCULATE THE HIGHEST CONFIDENCE NOW THAT OBJECTS
		// NOT IN SCOPE HAVE BEEN REMOVED
		highest_confidence = 0;

		for (index = 1; index <= objects; index++) {
			if (confidence[index] > highest_confidence) {
				highest_confidence = confidence[index];
			}
		}

		//printf("--- removing lower confidence objects\n");
		for (index = 1; index <= objects; index++) {
			/* REMOVE ANY OBJECTS THAT ARE NOT EQUAL
			   TO THE HIGHEST CONFIDENCE. */
			if (confidence[index] != FALSE) {
				if (confidence[index] < highest_confidence) {
					//printf("--- removing %s due to confidence of %d being under %d\n", object[index]->label, confidence[index], highest_confidence);
					confidence[index] = FALSE;
					matches--;
				}
			}
		}
	}

	if (matches == 0) {
		/* IF THIS LEAVES NO OBJECT, RETURN THE OBJECT THAT WAS PRESENT
		 * AND SET THE APPROPRIATE POINTERS. */
		if (prime_suspect != FALSE) {
			return (prime_suspect);
		} else {
			object_expected = TRUE;
			wp = backup_pointer;
			return (FALSE);
		}
	}

	if (matches == 1) {
		/* IF ONLY ONE POSSIBILITY REMAINS, RETURN THIS OBJECT AND SET THE
		 * APPROPRIATE POINTERS. */
		//printf("--- only one object left\n");
		for (index = 1; index <= objects; index++) {
			if (confidence[index] != FALSE) {
				return (index);
			}
		}
	}

	if (return_limit > 1) {
		/* THE PLURAL NAME OF AN OBJECT WAS USED OR A QUANTITY QUALIFIER */

		counter = 0;

		//printf("--- return_limit == TRUE\n");

		for (index = 1; index <= objects; index++) {
			if (confidence[index] != FALSE) {
				/* ADD EACH OBJECT TO multiple_resolved UNTIL
				 * THE return_limit IS REACHED */
				multiple_resolved[counter] = index;
				return_limit--;
				//printf("--- adding %s to multiple_resolved\n", object[index]->label);
				counter++;

				if (return_limit == 0) {
					break;
				}
			}
		}

		/* NULL TERMINATE THE LIST */
		multiple_resolved[counter] = 0;

		//printf("--- returning multiple objects\n");
		/* RETURN -1 TO INDICATED THERE ARE MULTIPLE OBJECTS */
		return (-1);
	}

	/* AN AMBIGUOUS REFERENCE WAS MADE. ATTEMPT TO CALL ALL THE disambiguate
	 * FUNCTIONS TO SEE IF ANY OF THE OBJECT WANTS TO TAKE PREFERENCE IN
	 * THIS CIRCUMSTANCE */
	int situation = noun_number;

	if (finding_from) {
		situation += 4;
	}

	/*
	for (index = 1; index <= objects; index++) {
	    if (confidence[index] != FALSE) {
	        strcpy(function_name, "disambiguate");
	        strcat(function_name, "_");
	        strcat(function_name, object[index]->label);
	        strcat(function_name, "<");
	        sprintf(temp_buffer, "%d", situation);
	        strcat(function_name, temp_buffer);

	        // CALL THE DISAMBIGUATION FUNCTION ATTACHED TO EACH OF THE
	        // POSSIBLE OBJECTS
	        int return_code = execute (function_name);

	        if (return_code == 1) {
	            // THIS OBJECT CAN CLAIMED OWNERSHIP OF THIS COMMAND
	            return (index);
	        } else if (return_code == -1) {
	            // THIS OBJECT HAS REJECTED OWNERSHIP OF THIS COMMAND
	            confidence[index] = FALSE;
	            matches--;
	        }
	    }
	}
	*/

	// CHECK IF ALL THE OBJECTS WERE REJECTED
	if (matches == 0) {
		return (prime_suspect);
	}

	// CHECK IF ONLY ONE OBJECT NOW REMAINS
	if (matches == 1) {
		/* IF ONLY ONE POSSIBILITY REMAINS, RETURN THIS OBJECT AND SET THE
		 * APPROPRIATE POINTERS. */
		//printf("--- only one object left\n");
		for (index = 1; index <= objects; index++) {
			if (confidence[index] != FALSE) {
				return (index);
			}
		}
	}

#if defined GLK || defined __NDS__
	/* NO OBJECT HAS CLAIMED OWNERSHIP, PROMPT THE PLAYER TO SPECIFY
	 * WHICH ONE THEY REQUIRE. */
	counter = 1;
	write_text(cstring_resolve("BY")->value);
	write_text(error_buffer);
	write_text(cstring_resolve("REFERRING_TO")->value);
	for (index = 1; index <= objects; index++) {
		if (confidence[index] != FALSE) {
			possible_objects[counter] = index;
			sprintf(text_buffer, "  [%d] ", counter);
			write_text(text_buffer);
			sentence_output(index, 0);
			write_text(temp_buffer);
			matches--;
			if (counter < 9)
				counter++;
			write_text("^");
		}
	}

	/* GET A NUMBER: don't insist, low = 1, high = counter */
	selection = get_number(FALSE, 1, counter - 1);

	if (selection == -1) {
		write_text(cstring_resolve("INVALID_SELECTION")->value);
		custom_error = TRUE;
		return (FALSE);
	} else {
		write_text("^");
		return (possible_objects[selection]);
	}
#else
	/* IF MORE THAT ONE OBJECT STILL REMAINS, PROMPT THE PLAYER TO SPECIFY
	 * WHICH ONE THEY REQUIRE. */
	write_text(cstring_resolve("WHEN_YOU_SAY")->value);
	write_text(error_buffer);
	write_text(cstring_resolve("MUST_SPECIFY")->value);
	for (index = 1; index <= objects; index++) {
		if (confidence[index] != FALSE) {
			sentence_output(index, 0);
			write_text(temp_buffer);
			matches--;
			if (matches == 0) {
				write_text(".");
				break;
			} else if (matches == 1) {
				write_text(cstring_resolve("OR_WORD")->value);
			} else
				write_text(", ");
		}
	}
	write_text("^");

	custom_error = TRUE;
	return (FALSE);
#endif
}

void diagnose() {
	if (custom_error) {
		TIME->value = FALSE;
		return;
	}
	if (word[wp] == NULL)
		write_text(cstring_resolve("INCOMPLETE_SENTENCE")->value);
	else if (object_expected && wp != 0) {
		write_text(cstring_resolve("UNKNOWN_OBJECT")->value);
		write_text(object_name);
		write_text(cstring_resolve("UNKNOWN_OBJECT_END")->value);
	} else {
		write_text(cstring_resolve("CANT_USE_WORD")->value);
		write_text(word[wp]);
		write_text(cstring_resolve("IN_CONTEXT")->value);
	}
	TIME->value = FALSE;
}

int scope(int index, const char *expected, int restricted) {
	/* THIS FUNCTION DETERMINES IF THE SPECIFIED OBJECT IS IN THE SPECIFIED
	 * SCOPE - IT RETURNS TRUE IF SO, FALSE IF NOT. */

	int temp = 0;

	/* WHEN THE ARGUMENT restricted IS TRUE IT HAS A MORE LIMITED
	 * SENSE OF WHAT IS ACCEPTABLE */

	if (!strcmp(expected, "*held") || !strcmp(expected, "**held")) {
		if (object[index]->PARENT == HELD) {
			return (TRUE);
		} else if (object[index]->MASS >= HEAVY) {
			/* ALLOW AND OBJECT TO BE CONSIDERED HELD IF IT HAS A
			 * MASS OF HEAVY OR GREATER AND ITS DIRECT PARENT IS
			 * BEING HELD. IE, A LABEL ON A JAR CAN BE SHOWN BECAUSE
			 * THE JAR IS BEING HELD. */
			temp = object[index]->PARENT;
			if (temp > 0 && temp < objects) {
				if (object[temp]->PARENT == HELD) {
					return (TRUE);
				}
			}
			return (FALSE);
		} else {
			return (FALSE);
		}
	} else if (!strcmp(expected, "*location")) {
		if (object[index]->attributes & LOCATION) {
			return (TRUE);
		} else {
			return (FALSE);
		}
	} else if (!strcmp(expected, "*here") || !strcmp(expected, "**here")) {
		if (object[index]->PARENT == HERE || index == HERE) {
			/* THE OBJECT IN QUESTION IS IN THE PLAYER'S CURRENT LOCATION
			 * OR IS THE PLAYER'S CURRENT LOCATION. */
			return (TRUE);
		} else if (object[index]->PARENT == HELD) {
			/* IT IS ONLY A PROBLEM FOR THE OBJECT TO BE AN IMMEDIATE CHILD
			 * OF THE PLAYER. THE PLAYER CAN STILL TAKE AN OBJECT THAT IS IN
			 * SOMETHING THEY ARE CARRYING */
			return (FALSE);
		} else {
			/* IS THE OBJECT A CHILD OF THE CURRENT LOCATION SOMEHOW */
			return (parent_of(HERE, index, restricted));
		}
	} else if (!strcmp(expected, "*anywhere") || !strcmp(expected, "**anywhere")) {
		return (TRUE);
	} else if (!strcmp(expected, "*inside") || !strcmp(expected, "**inside")) {
		if (object_list[0][0] > 0 && object_list[0][0] < objects) {
			return (parent_of(object_list[0][0], index, restricted));
		} else {
			// THERE IS NO PREVIOUS OBJECT SO TREAT THIS LIKE A *here
			return (parent_of(HERE, index, restricted));
		}
	} else if (!strcmp(expected, "*present") || !strcmp(expected, "**present")) {
		if (index == HERE) {
			return (TRUE);
		} else {
			if (find_parent(index)) {
				return (TRUE);
			} else {
				return (FALSE);
			}
		}
	} else {
		unkscorun(expected);
		return (FALSE);
	}
}

int find_parent(int index) {
	/* THIS FUNCTION WILL SET THE GLOBAL VARIABLE parent TO
	 * THE OBJECT THAT IS AT THE TOP OF THE POSSESSION TREE.
	 * IT WILL RETURN TRUE IF THE OBJECT IS VISIBLE TO THE
	 * PLAYER */
	//printf("--- find parent of %s\n", object[index]->label);

	if (!(object[index]->attributes & LOCATION) &&
	        object[index]->PARENT != NOWHERE) {

		parent = object[index]->PARENT;
		//printf("--- parent is %s\n", object[parent]->label);

		if (index == parent) {
			/* THIS OBJECT HAS ITS PARENT SET TO ITSELF */
			sprintf(error_buffer, SELF_REFERENCE, executing_function->name, object[index]->label);
			log_error(error_buffer, PLUS_STDOUT);
			return (FALSE);
		} else  if (!(object[parent]->attributes & LOCATION)
		            && ((object[parent]->attributes & CLOSED && object[parent]->attributes & CONTAINER)
		                || object[parent]->attributes & CONCEALING)) {
			//printf("--- %s is closed, so return FALSE\n", object[parent]->label);
			return (FALSE);
		} else if (parent == HERE || parent == HELD) {
			/* THE OBJECT IS THE PLAYER'S CURRENT LOCATION OR BEING HELD */
			return (TRUE);
		} else {
			if (object[parent]->attributes & LOCATION) {
				//printf("--- %s is a location, so dont recuse\n", object[parent]->label);
				return (FALSE);
			} else {
				//printf("--- %s isnt a location, so recuse\n", object[parent]->label);
				return (find_parent(parent));
			}
		}
	} else {
		if (index == HERE)
			/* THE OBJECT IS THE PLAYER'S CURRENT LOCATION. */
			return (TRUE);
		else
			return (FALSE);
	}
}

int parent_of(int parent_, int child, int restricted) {
	/* THIS FUNCTION WILL CLIMB THE OBJECT TREE STARTING AT 'CHILD' UNTIL
	 * 'PARENT' IS REACHED (RETURN TRUE), OR THE TOP OF THE TREE OR A CLOSED
	 * OR CONCEALING OBJECT IS REACHED (RETURN FALSE). */

	/* restricted ARGUMENT TELLS FUNCTION TO IGNORE OBJECT IF IT IS IN AN
	 * OBJECT WITH A mass OF heavy OR LESS THAT IS NOT THE SUPPLIED
	 * PARENT ie. DON'T ACCEPT OBJECTS IN SUB OBJECTS */

	int             index;

	//printf("--- parent_ is %s, child is %s\n", object[parent_]->label, object[child]->label);
	if (child == parent_) {
		return (TRUE);
	} else if (!(object[child]->attributes & LOCATION) &&
	           object[child]->PARENT != NOWHERE) {
		/* STORE THE CHILDS PARENT OBJECT */
		index = object[child]->PARENT;
		//printf("--- %s is the parent_ of %s\n", object[index]->label, object[child]->label);

		if (index == child) {
			/* THIS CHILD HAS IT'S PARENT SET TO ITSELF */
			sprintf(error_buffer, SELF_REFERENCE, executing_function->name, object[index]->label);
			log_error(error_buffer, PLUS_STDOUT);
			//printf("--- self parent_.\n");
			return (FALSE);
		} else  if (!(object[index]->attributes & LOCATION)
		            && ((object[index]->attributes & CLOSED && object[index]->attributes & CONTAINER)
		                || object[index]->attributes & CONCEALING)) {
			/* THE CHILDS PARENT IS CLOSED OR CONCEALING - CANT BE SEEN */
			//printf("--- parent_ %s is closed\n", object[index]->label);
			return (FALSE);
		} else if (restricted && object[index]->MASS < HEAVY && index != parent_) {
			//printf("--- scenery object.\n");
			return (FALSE);
		} else {
			//printf("--- comparing %s with %s\n", object[index]->label, object[parent_]->label);
			if (index == parent_) {
				/* YES, IS PARENT OF CHILD */
				return (TRUE);
			} else if (object[index]->attributes & LOCATION) {
				return (FALSE);
			} else {
				/* KEEP LOOKING UP THE TREE TILL THE CHILD HAS NO MORE
				 * PARENTS */
				return (parent_of(parent_, index, restricted));
			}
		}
	} else {
		/* THE SPECIFIED OBJECT HAS NO PARENT */
		return (FALSE);
	}
}

} // End of namespace JACL
} // End of namespace Glk