aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/tads/tads2/os.h
blob: 5dcc83c935565acfc43570c5f575a7bc18e61763 (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
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
/* 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.
 *
 */

/* Portable interfaces to OS-specific functions
 *
 * This file defines interfaces to certain functions that must be called
 * from portable code, but which must have system-specific implementations.
 */

#ifndef GLK_TADS_TADS2_OS
#define GLK_TADS_TADS2_OS

#include "common/system.h"
#include "glk/tads/osfrobtads.h"
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/appctx.h"

namespace Glk {
namespace TADS {
namespace TADS2 {


/* ------------------------------------------------------------------------ */
/*
 *   A note on character sets:
 *   
 *   Except where noted, all character strings passed to and from the
 *   osxxx functions defined herein use the local operating system
 *   representation.  On a Windows machine localized to Eastern Europe,
 *   for example, the character strings passed to and from the osxxx
 *   functions would use single-byte characters in the Windows code page
 *   1250 representation.
 *   
 *   Callers that use multiple character sets must implement mappings to
 *   and from the local character set when calling the osxxx functions.
 *   The osxxx implementations are thus free to ignore any issues related
 *   to character set conversion or mapping.
 *   
 *   The osxxx implementations are specifically not permitted to use
 *   double-byte Unicode as the native character set, nor any other
 *   character set where a null byte could appear as part of a non-null
 *   character.  In particular, callers may assume that null-terminated
 *   strings passed to and from the osxxx functions contain no embedded
 *   null bytes.  Multi-byte character sets (i.e., character sets with
 *   mixed single-byte and double-byte characters) may be used as long as
 *   a null byte is never part of any multi-byte character, since this
 *   would guarantee that a null byte could always be taken as a null
 *   character without knowledge of the encoding or context.  
 */

/* ------------------------------------------------------------------------ */
/*
 *   "Far" Pointers.  Most platforms can ignore this.  For platforms with
 *   mixed-mode addressing models, where pointers of different sizes can
 *   be used within a single program and hence some pointers require
 *   qualification to indicate that they use a non-default addressing
 *   model, the keyword OSFAR should be defined to the appropriate
 *   compiler-specific extension keyword.
 *   
 *   If you don't know what I'm talking about here, you should just ignore
 *   it, because your platform probably doesn't have anything this
 *   sinister.  As of this writing, this applies only to MS-DOS, and then
 *   only to 16-bit implementations that must interact with other 16-bit
 *   programs via dynamic linking or other mechanisms.  
 */


/* ------------------------------------------------------------------------ */
/*
 *   ANSI C99 exact-size integer types.
 *   
 *   C99 defines a set of integer types with exact bit sizes, named intXX_t
 *   for a signed integer with XX bits, and uintXX_t for unsigned.  XX can be
 *   8, 16, 32, or 64.  TADS uses the 16- and 32-bit sizes, so each platform
 *   is responsible for defining the following types:
 *   
 *.    int16_t   - a signed integer type storing EXACTLY 16 bits
 *.    uint16_t  - an unsigned integer type storing EXACTLY 16 bits
 *.    int32_t   - a signed integer type storing EXACTLY 32 bits
 *.    uint32_t  - an unsigned integer type storing EXACTLY 32 bits
 *   
 *   Many modern compilers provide definitions for these types via the
 *   standard header stdint.h.  Where stdint.h is provided, the platform code
 *   can merely #include <stdint.h>.
 *   
 *   For compilers where stdint.h isn't available, you must provide suitable
 *   typedefs.  Note that the types must be defined with the exact bit sizes
 *   specified; it's not sufficient to use a bigger type, because we depend
 *   in some cases on overflow and sign extension behavior at the specific
 *   bit size.
 */


/* ------------------------------------------------------------------------ */
/*
 *   Thread-local storage (TLS).
 *   
 *   When TADS is compiled with threading support, it requires some variables
 *   to be "thread-local".  This means that the variables have global scope
 *   (so they're not stored in "auto" variables on the stack), but each
 *   thread has a private copy of each such variable.
 *   
 *   Nearly all systems that support threads also support thread-local
 *   storage.  Like threading support itself, though, TLS support is at
 *   present implemented only in non-portable OS APIs rather than standard C
 *   language features.  TLS is a requirement if TADS is compiled with
 *   threading, but it's not needed for non-threaded builds.  TADS only
 *   requires threading at present (version 3.1) for its network features;
 *   since these features are optional, systems that don't have threading and
 *   TLS support will simply need to disable the network features, which will
 *   allow all of the threading and TLS definitions in osifc to be omitted.
 *   
 *   There appear to be two common styles of TLS programming models.  The
 *   first provides non-standard compiler syntax for declarative creation of
 *   thread-local variables.  The Microsoft (on Windows) and Gnu compilers
 *   (on Linux and Unix) do this: they provide custom storage class modifiers
 *   for declaring thread locals (__declspec(thread) for MSVC, __thread for
 *   gcc).  Compilers that support declarative thread locals handle the
 *   implementation details through code generation, so the program merely
 *   needs to add the special TLS storage class qualifier to an otherwise
 *   ordinary global variable declaration, and then can access the thread
 *   local as though it were an ordinary global.
 *   
 *   The second programming model is via explicit OS API calls to create,
 *   initialize, and access thread locals.  pthreads provides such an API, as
 *   does Win32.  In fact, when you use the declarative syntax with MSVC or
 *   gcc, the compiler generates the appropriate API calls, but the details
 *   are transparent to the program; in contrast, when using pthreads
 *   directly, the program must actively call the relevant APIs.
 *   
 *   It's probably the case that every system that has compiler-level support
 *   for declarative thread local creation also has procedural APIs, so the
 *   simplest way to abstract the platform differences would be to do
 *   everything in terms of APIs.  However, it seems likely that compilers
 *   with declarative syntax might be able to generate more efficient code,
 *   since optimizers always benefit from declarative information.  So we'd
 *   like to use declarative syntax whenever it's available, but fall back on
 *   explicit API calls when it's not.  So our programming model is a union
 *   of the two styles:
 *   
 *   1. For each thread local, declare the thread local:
 *.      OS_DECL_TLS(char *, my_local);
 *   
 *   2. At main program startup (for the main thread only), initialize each
 *   thread local:
 *.      os_tls_create(my_local);
 *   
 *   3. Never get or set the value of a thread local directly; instead, use
 *   the get/set functions:
 *.      char *x = os_tls_get(char *, my_local);
 *.      os_tls_set(my_local, "hello");
 *   
 *   One key feature of this implementation is that each thread local is
 *   stored as a (void *) value.  We do it this way to allow a simple direct
 *   mapping to the pthreads APIs, since that's going to be the most common
 *   non-declarative implementation.  This means that a thread local variable
 *   can contain any pointer type, but *only* a pointer type.  The standard
 *   pattern for dealing with anything more ocmplex is the same as in
 *   pthreads: gather up the data into a structure, malloc() an instance of
 *   that structure at entry to each thread (including the main thread), and
 *   os_tls_set() the variable to contain a pointer to that structure.  From
 *   then on, use os_tls_set(my_struct *, my_local)->member to access the
 *   member variables in the structure.  And finally, each thread must delete
 *   the structure at thread exit.
 */

/*   
 *   
 *   Declare a thread local.
 *   
 *   - For compilers that support declarative TLS variables, the local OS
 *   headers should use the compiler support by #defining OS_DECL_TLS to the
 *   appropriate local declarative keyword.
 *   
 *   - For systems without declarative TLS support but with TLS APIs, the
 *   global declared by this macro actually stores the slot ID (what pthreads
 *   calls the "key") for the variable.  This macro should therefore expand
 *   to a declaration of the appropriate API type for a slot ID; for example,
 *   on pthreads, #define OS_DECL_TLS(t, v) pthread_key_t v.
 *   
 *   - For builds with no thread support, simply #define this to declare the
 *   variable as an ordinary global: #define OS_DECL_TLS(t, v) t v.
 */
/* #define OS_DECL_TLS(typ, varname)  __thread typ varname */

/*
 *   For API-based systems without declarative support in the compiler, the
 *   main program startup code must explicitly create a slot for each thread-
 *   local variable by calling os_tls_create().  The API returns a slot ID,
 *   which is shared among threads and therefore can be stored in an ordinary
 *   global variable.  OS_DECL_TLS will have declared the global variable
 *   name in this case as an ordinary global of the slot ID type.  The
 *   os_tls_create() macro should therefore expand to a call to the slot
 *   creation API, storing the new slot ID in the global.
 *   
 *   Correspondingly, before the main thread exits, it should delete each
 *   slot it created, b calling os_tls_delete().
 *   
 *   For declarative systems, there's no action required here, so these
 *   macros can be defined to empty.
 */
/* #define os_tls_create(varname)  pthread_key_create(&varname, NULL) */
/* #define os_tls_delete(varname)  pthread_key_delete(varname) */


/*
 *   On API-based systems, each access to get or set the thread local
 *   requires an API call, using the slot ID stored in the actual global to
 *   get the per-thread instance of the variable's storage.
 *.    #define os_tls_get(typ, varname) ((typ)pthread_getspecific(varname))
 *.    #define os_tls_set(varname, val) pthread_setspecific(varname, val)
 *   
 *   On declarative systems, the global variable itself is the thread local,
 *   so get/set can be implemented as direct access to the variable.
 *.    #define os_tls_get(typ, varname) varname
 *.    #define os_tls_set(varname, val) varname = (val)
 */

/*
 *   Common TLS definitions - declarative thread locals
 *   
 *   For systems with declarative TLS support in the compiler, the OS header
 *   can #define OS_DECLARATIVE_TLS to pick up suitable definitions for the
 *   os_tls_xxx() macros.  The OS header must separately define OS_DECL_TLS
 *   as appropriate for the local system.
 */
#ifdef OS_DECLARATIVE_TLS
#define os_tls_create(varname)
#define os_tls_delete(varname)
#define os_tls_get(typ, varname) varname
#define os_tls_set(varname, val) varname = (val)
#endif

/*
 *   Common TLS definitions - pthreads
 *   
 *   For pthreads systems without declarative TLS support in the compiler,
 *   the OS header can simply #define OS_PTHREAD_TLS to pick up the standard
 *   definitions below. 
 */
#ifdef OS_PTHREAD_TLS
#include <pthread.h>
#define OS_DECL_TLS(typ, varname) pthread_key_t varname
#define os_tls_create(varname) pthread_key_create(&varname, NULL)
#define os_tls_delete(varname) pthread_key_delete(varname)
#define os_tls_get(typ, varname) ((typ)pthread_getspecific(varname))
#define os_tls_set(varname, val) pthread_setspecific(varname, val)
#endif


/* ------------------------------------------------------------------------ */
/*
 *   <time.h> definitions.
 *   
 *   os_time() should act like Unix time(), returning the number of seconds
 *   elapsed since January 1, 1970 at midnight UTC.
 *   
 *   The original Unix <time.h> package defined time_t as a 32-bit signed
 *   int, and many subsequent C compilers on other platforms followed suit.
 *   A signed 32-bit time_t has the well-known year-2038 problem; some later
 *   C compilers tried to improve matters by using an unsigned 32-bit time_t
 *   instead, but for many purposes this is even worse since it can't
 *   represent any date before 1/1/1970.  *Most* modern compilers solve the
 *   problem once and for all (for 300 billion years in either direction of
 *   1/1/1970, anyway - enough to represent literally all of eternity in most
 *   current cosmological models) by defining time_t as a signed 64-bit int.
 *   But some compilers stubbornly stick to the old 32-bit time_t even in
 *   newer versions, for the sake of compatibility with older code that might
 *   be lax about mixing time_t's with ordinary int's.  E.g., MSVC2003 does
 *   this.  Fortunately, some of these compilers (such as MSVC2003 again)
 *   also define a parallel, transitional set of 64-bit time functions that
 *   you can use by replacing all references to the standard time_t and
 *   related names with the corresponding 64-bit names.
 *   
 *   We'd really like to use a 64-bit time_t wherever we can - the TADS
 *   release cycle can be a bit slow, and we don't want 2038 to sneak up on
 *   us and catch us unawares.  So for those compilers that offer a choice of
 *   32 or 64 bits, we'd like to select the 64 bit version.  To facilitate
 *   this, we define covers here for the time.h types and functions that we
 *   use.  On platforms where the regular time_t is already 64 bits, or where
 *   there's no 64-bit option at all, you can simply do nothing - the
 *   defaults defined here use the standard time_t typedef and functions, so
 *   that's what you'll get if you don't define these in the OS-specific
 *   headers for your platform.  For compilers that provide both a 32-bit
 *   time_t and a 64-bit other_time_t, the OS headers should #define these
 *   macros in terms of those compiler-specific 64-bit names.
 */
#ifndef os_time_t
# define os_time_t        TimeDate
# define os_gmtime(t)     gmtime(t)
# define os_localtime(t)  localtime(t)
# define os_time(t)       time(t)
#endif

/*
 *   Initialize the time zone.  This routine is meant to take care of any
 *   work that needs to be done prior to calling localtime() and other
 *   time-zone-dependent routines in the run-time library.  For DOS and
 *   Windows, we need to call the run-time library routine tzset() to set up
 *   the time zone from the environment; most systems shouldn't need to do
 *   anything in this routine.  It's sufficient to call this once during the
 *   process lifetime, since it's meant to perform static initialization that
 *   lasts as long as the process is running.
 */
#ifndef os_tzset
void os_tzset(void);
#endif

/*
 *   Higher-precision time.  This retrieves the same time information as
 *   os_time() (i.e., the elapsed time since the standard Unix Epoch, January
 *   1, 1970 at midnight UTC), but retrieves it with the highest precision
 *   available on the local system, up to nanosecond precision.  If less
 *   precision is available, that's fine; just return the time to the best
 *   precision available, but expressed in terms of the number of
 *   nanoseconds.  For example, if you can retrieve milliseconds, you can
 *   convert that to nanoseconds by multiplying by 1,000,000.
 *   
 *   On return, fills in '*seconds' with the number of whole seconds since
 *   the Epoch, and fills in '*nanoseconds' with the fractional portion,
 *   expressed in nanosceconds.  Note that '*nanoseconds' is merely the
 *   fractional portion of the time, so 0 <= *nanoseconds < 1000000000.
 */
void os_time_ns(os_time_t *seconds, long *nanoseconds);

/*
 *   Get the local time zone name, as a location name in the IANA zoneinfo
 *   database.  For example, locations using US Pacific Time should return
 *   "America/Los_Angeles".
 *   
 *   Returns true if successful, false if not.  If the local operating system
 *   doesn't have a way to obtain this information, or if it's not available
 *   in the local machine's configuration, this returns false.
 *   
 *   The zoneinfo database is also known as the Olson or TZ (timezone)
 *   database; it's widely used on Unix systems as the definitive source of
 *   local time zone settings.  See http://www.iana.org/time-zones for more
 *   information.
 *   
 *   On many Unix systems, the TZ environment variable contains the zoneinfo
 *   zone name when its first character is ':'.  Windows uses a proprietary
 *   list of time zone names that can be mapped to zoneinfo names via a
 *   hand-coded list (such a list is maintained in the Unicode CLDR; our
 *   Windows implementation uses the CLDR list to generate the mapping).
 *   MacOS X uses zoneinfo keys directly; /etc/localtime is a link to the
 *   zoneinfo file for the local zone as set via the system preferences.
 *   
 *   os_tzset() must be invoked at some point before this routine is called.
 */
int os_get_zoneinfo_key(char *buf, size_t buflen);

/*
 *   Get a description of the local time zone.  Fills in '*info' with the
 *   available information.  Returns true on success, false on failure.
 *   
 *   See osstzprs.h/.c for a portable implementation of a parser for
 *   POSIX-style TZ strings.  That can serve as a full implementation of this
 *   function for systems that use the POSIX TZ environment variable syntax
 *   to specify the timezone.  (That routine simply parses a string from any
 *   source, so it can be used to parse the TZ syntax even on systems where
 *   the string comes from somewhere other than the TZ environment variable.)
 *   
 *   os_tzset() must be invoked at some point before this routine is called.
 *   
 *   The following two structures are used for the return information:
 *   
 *   os_tzrule_t - Timezone Rule structure.  This describes a rule for an
 *   annual transition between daylight savings time and standard time in a
 *   time zone.  Most timezones that have recurring standard/daylight changes
 *   require two of these rules, one for switching to daylight time in the
 *   spring and one for switching to standard time in the fall.
 *   
 *   os_tzinfo_t - Timezone Information structure.  This describes a
 *   timezone's clock settings, name(s), and rules for recurring annual
 *   changes between standard time and daylight time, if applicable.
 */
struct os_tzrule_t
{
    /* 
     *   Day of year, 1-365, NEVER counting Feb 29; set to 0 if not used.
     *   Corresponds to the "J" format in Unix TZ strings.  (Called "Julian
     *   day" in the POSIX docs, thus the "J", even though it's a bit of a
     *   misnomer.)(Because of the invariance of the mapping from J-number to
     *   date, this is just an obtuse way of specifying a month/day date.
     *   But even so, we'll let the OS layer relay this back to us in
     *   J-number format and count on the portable caller to work out the
     *   date, rather than foisting that work on each platform
     *   implementation.)
     */
    int jday;

    /*
     *   Day of year, 1-366, counting Feb 29 on leap years; set to 0 if not
     *   used; ignored if 'jday' is nonzero.  This corresponds to the Julian
     *   day sans "J" in TZ strings (almost - that TZ format uses 0-365 as
     *   its range, so bump it up by one when parsing a TZ string).  This
     *   format is even more obtuse than the J-day format, in that it doesn't
     *   even have an invariant month/day mapping (not after day 59, anyway -
     *   day 60 is either February 29 or March 1, depending on the leapness
     *   of the year, and every day after that is similarly conditional).  As
     *   far as I can tell, no one uses this option, so I'm not sure why it
     *   exists.  The zoneinfo source format doesn't have a way to represent
     *   it, which says to me that no one has ever used it in a statutory DST
     *   start/end date definition in the whole history of time zones around
     *   the world, since the whole history of time zones around the world is
     *   exactly what the zoneinfo database captures in exhaustive and
     *   painstaking detail.  If anyone had ever used it in defining a time
     *   zone, zoneinfo would have an option for it.  My guess is that it's a
     *   fossilized bug from some early C RTL that's been retained out of an
     *   abundance of caution vis-a-vis compatibility, and was entirely
     *   replaced in practice by the J-number format as soon as someone
     *   noticed the fiddly leap year behavior.  But for the sake of
     *   completeness...
     */
    int yday;
    
    /* 
     *   The month (1-12), week of the month, and day of the week (1-7 for
     *   Sunday to Saturday).  Week 1 is the first week in which 'day'
     *   occurs, week 2 is the second, etc.; week 5 is the last occurrence of
     *   'day' in the month.  These fields are used for "second Sunday in
     *   March" types of rules.  Set these to zero if they're not used;
     *   they're ignored in any case if 'jday' or 'yday' are non-zero.
     */
    int month;
    int week;
    int day;

    /* time of day, in seconds after midnight (e.g., 2AM is 120 == 2*60*60) */
    int time;
};
struct os_tzinfo_t
{
    /*
     *   The local offset from GMT, in seconds, for standard time and
     *   daylight time in this zone.  These values are positive for zones
     *   east of GMT and negative for zones west: New York standard time
     *   (EST) is 5 hours west of GMT, so its offset is -5*60*60.
     *   
     *   Set both of these fields (if possible) regardless of whether
     *   standard or daylight time is currently in effect in the zone.  The
     *   caller will select which offset to use based on the start/end rules,
     *   or based on the 'is_dst' flag if no rules are available.
     *   
     *   If it's only possible to determine the current wall clock offset, be
     *   it standard or daylight time, and it's not possible to determine the
     *   time difference between the two, simply set both of these to the
     *   current offset.  This information isn't available from the standard
     *   C library, and many OS APIs also lack it.  
     */
    int32_t std_ofs;
    int32_t dst_ofs;

    /*
     *   The abbreviations for the local zone's standard time and daylight
     *   time, respectively, when displaying date/time values.  E.g., "EST"
     *   and "EDT" for US Eastern Time.  If the zone doesn't observe daylight
     *   time (it's on standard time year round), set dst_abbr to an empty
     *   string.
     *   
     *   As with std_ofs and dst_ofs, you can set both of these to the same
     *   string if it's only possible to determine the one that's currently
     *   in effect.
     */
    char std_abbr[16];
    char dst_abbr[16];

    /*
     *   The ongoing rules for switching between daylight and standard time
     *   in this zone, if available.  'dst_start' is the date when daylight
     *   savings starts, 'dst_end' is the date when standard time resumes.
     *   Set all fields to 0 if the start/stop dates aren't available, or the
     *   zone is on standard time year round.
     */
    struct os_tzrule_t dst_start;
    struct os_tzrule_t dst_end;

    /* 
     *   True -> the zone is CURRENTLY on daylight savings time; false means
     *   it's currently on standard time.
     *   
     *   This is only used if the start/end rules aren't specified.  In the
     *   absence of start/end rules, there's no way to know when the current
     *   standard/daylight phase ends, so we'll have to assume that the
     *   current mode is in effect permanently.  In this case, the caller
     *   will use only be able to use the offset and abbreviation for the
     *   current mode and will have to ignore the other one.
     */
    int is_dst;
};
int os_get_timezone_info(struct os_tzinfo_t *info);


/*
 *   Get the current system high-precision timer.  This function returns a
 *   value giving the wall-clock ("real") time in milliseconds, relative to
 *   any arbitrary zero point.  It doesn't matter what this value is relative
 *   to -- the only important thing is that the values returned by two
 *   different calls should differ by the number of actual milliseconds that
 *   have elapsed between the two calls.  This might be the number of
 *   milliseconds since the computer was booted, since the current user
 *   logged in, since midnight of the previous night, since the program
 *   started running, since 1-1-1970, etc - it doesn't matter what the epoch
 *   is, so the implementation can use whatever's convenient on the local
 *   system.
 *   
 *   True millisecond precision isn't required.  Each implementation should
 *   simply use the best precision available on the system.  If your system
 *   doesn't have any kind of high-precision clock, you can simply use the
 *   time() function and multiply the result by 1000 (but see the note below
 *   about exceeding 32-bit precision).
 *   
 *   However, it *is* required that the return value be in *units* of
 *   milliseconds, even if your system clock doesn't have that much
 *   precision; so on a system that uses its own internal clock units, this
 *   routine must multiply the clock units by the appropriate factor to yield
 *   milliseconds for the return value.
 *   
 *   It is also required that the values returned by this function be
 *   monotonically increasing.  In other words, each subsequent call must
 *   return a value that is equal to or greater than the value returned from
 *   the last call.  On some systems, you must be careful of two special
 *   situations.
 *   
 *   First, the system clock may "roll over" to zero at some point; for
 *   example, on some systems, the internal clock is reset to zero at
 *   midnight every night.  If this happens, you should make sure that you
 *   apply a bias after a roll-over to make sure that the value returned from
 *   this return continues to increase despite the reset of the system clock.
 *   
 *   Second, a 32-bit signed number can only hold about twenty-three days
 *   worth of milliseconds.  While it seems unlikely that a TADS game would
 *   run for 23 days without a break, it's certainly reasonable to expect
 *   that the computer itself may run this long without being rebooted.  So,
 *   if your system uses some large type (a 64-bit number, for example) for
 *   its high-precision timer, you may want to store a zero point the very
 *   first time this function is called, and then always subtract this zero
 *   point from the large value returned by the system clock.  If you're
 *   using time(0)*1000, you should use this technique, since the result of
 *   time(0)*1000 will almost certainly not fit in 32 bits in most cases.  
 */
long os_get_sys_clock_ms(void);


/* ------------------------------------------------------------------------ */
/*
 *   Hardware Configuration.  Define the following functions appropriately
 *   for your hardware.  For efficiency, these functions should be defined
 *   as macros if possible.
 *   
 *   Note that these hardware definitions are independent of the OS, at
 *   least to the extent that your OS can run on multiple types of
 *   hardware.  So, rather than combining these definitions into your
 *   osxxx.h header file, we recommend that you put these definitions in a
 *   separate h_yyy.h header file, which can be configured into os.h with
 *   an appropriate "_M_yyy" preprocessor symbol.  Refer to os.h for
 *   details of configuring the hardware include file.  
 */

/* 
 *   Round a size up to worst-case alignment boundary.  For example, on a
 *   platform where the largest type must be aligned on a 4-byte boundary,
 *   this should round the value up to the next higher mutliple of 4 and
 *   return the result.  
 */
/* size_t osrndsz(size_t siz); */

/* 
 *   Round a pointer up to worst-case alignment boundary. 
 */
/* void *osrndpt(void *ptr); */

/* 
 *   Read an unaligned portable unsigned 2-byte value, returning an int
 *   value.  The portable representation has the least significant byte
 *   first, so the value 0x1234 is represented as the byte 0x34, followed
 *   by the byte 0x12.
 *   
 *   The source value must be treated as unsigned, but the result is
 *   signed.  This is significant on 32- and 64-bit platforms, because it
 *   means that the source value should never be sign-extended to 32-bits.
 *   For example, if the source value is 0xffff, the result is 65535, not
 *   -1.  
 */
/* int osrp2(unsigned char *p); */

/* 
 *   Read an unaligned portable signed 2-byte value, returning int.  This
 *   differs from osrp2() in that this function treats the source value as
 *   signed, and returns a signed result; hence, on 32- and 64-bit
 *   platforms, the result must be sign-extended to the int size.  For
 *   example, if the source value is 0xffff, the result is -1.  
 */
/* int osrp2s(unsigned char *p); */

/* 
 *   Write unsigned int to unaligned portable 2-byte value.  The portable
 *   representation stores the low-order byte first in memory, so
 *   oswp2(0x1234) should result in storing a byte value 0x34 in the first
 *   byte, and 0x12 in the second byte.  
 */
/* void oswp2(unsigned char *p, unsigned int i); */

/*
 *   Write signed int to unaligned portable 2-byte value.  Negative values
 *   must be stored in two's complement notation.  E.g., -1 is stored as
 *   FF.FF, -32768 is stored as 00.80 (little-endian).  
 *   
 *   Virtually all modern hardware uses two's complement notation as the
 *   native representation, which makes this routine a trivial synonym of
 *   osrp2() (i.e., #define oswp2s(p,i) oswp2(p,i)).  We distinguish the
 *   signed version on the extremely off chance that TADS is ever ported to
 *   wacky hardware with a different representation for negative integers
 *   (one's complement, sign bit, etc).  
 */
/* void oswp2s(unsigned char *p, int i); */

/* 
 *   Read an unaligned unsigned portable 4-byte value, returning long.  The
 *   underlying value should be considered signed, and the result is signed.
 *   The portable representation stores the bytes starting with the least
 *   significant: the value 0x12345678 is stored with 0x78 in the first byte,
 *   0x56 in the second byte, 0x34 in the third byte, and 0x12 in the fourth
 *   byte.
 */
/* unsigned long osrp4(unsigned char *p); */

/*
 *   Read an unaligned signed portable 4-byte value, returning long. 
 */
/* long osrp4s(unsigned char *p); */

/* 
 *   Write an unsigned long to an unaligned portable 4-byte value.  The
 *   portable representation stores the low-order byte first in memory, so
 *   0x12345678 is written to memory as 0x78, 0x56, 0x34, 0x12.  
 */
/* void oswp4(unsigned char *p, unsigned long l); */

/*
 *   Write a signed long, using little-endian byte order and two's complement
 *   notation for negative numbers.  This is a trivial synonym for oswp4()
 *   for all platforms with native two's complement arithmetic (which is
 *   virtually all modern platforms).  See oswp2s() for more discussion.
 */
/* void oswp4s(unsigned char *p, long l); */

/*
 *   For convenience and readability, the 1-byte integer (signed and
 *   unsigned) equivalents of the above.
 */
#define osrp1(p) (*(unsigned char *)(p))
#define osrp1s(p) (*(signed char *)(p))
#define oswp1(p, b) (*(unsigned char *)(p) = (b))
#define oswp1s(p, b) (*(signed char *)(p) = (b))


/* ------------------------------------------------------------------------ */
/*
 *   varargs va_copy() extension.
 *   
 *   On some compilers, va_list is a reference type.  This means that if a
 *   va_list value is passed to a function that uses va_arg() to step through
 *   the referenced arguments, the caller's copy of the va_list might be
 *   updated on return.  This is problematic in cases where the caller needs
 *   to use the va_list again in another function call, since the va_list is
 *   no longer pointing to the first argument for the second call.  C99 has a
 *   solution in the form of the va_copy() macro.  Unfortunately, this isn't
 *   typically available in pre-C99 compilers, and isn't standard in *any*
 *   C++ version.  We thus virtualize it here in a macro.
 *   
 *   os_va_copy() has identical semantics to C99 va_copy().  A matching call
 *   to os_va_copy_end() must be made for each call to os_va_copy() before
 *   the calling function returns; this has identical semantics to C99
 *   va_end().
 *   
 *   Because our semantics are identical to the C99 version, we provide a
 *   default definition here for compilers that define va_copy().  Platform
 *   headers must provide suitable definitions only if their compilers don't
 *   have va_copy().  We also provide a definition for GCC compilers that
 *   define the private __va_copy macro, which also has the same semantics.
 */
#ifdef va_copy
# define os_va_copy(dst, src) va_copy(dst, src)
# define os_va_copy_end(dst)  va_end(dst)
#else
# if defined(__GNUC__) && defined(__va_copy)
#  define os_va_copy(dst, src) __va_copy(dst, src)
#  define os_va_copy_end(dst)  va_end(dst)
# endif
#endif



/* ------------------------------------------------------------------------ */
/*
 *   Platform Identifiers.  You must define the following macros in your
 *   osxxx.h header file:
 *   
 *   OS_SYSTEM_NAME - a string giving the system identifier.  This string
 *   must contain only characters that are valid in a TADS identifier:
 *   letters, numbers, and underscores; and must start with a letter or
 *   underscore.  For example, on MS-DOS, this string is "MSDOS".
 *   
 *   OS_SYSTEM_LDESC - a string giving the system descriptive name.  This
 *   is used in messages displayed to the user.  For example, on MS-DOS,
 *   this string is "MS-DOS".  
 */


/* ------------------------------------------------------------------------ */
/*
 *   Message Linking Configuration.  You should #define ERR_LINK_MESSAGES
 *   in your osxxx.h header file if you want error messages linked into
 *   the application.  Leave this symbol undefined if you want an external
 *   message file. 
 */


/* ------------------------------------------------------------------------ */
/*
 *   Program Exit Codes.  These values are used for the argument to exit()
 *   to conform to local conventions.  Define the following values in your
 *   OS-specific header:
 *   
 *   OSEXSUCC - successful completion.  Usually defined to 0.
 *.  OSEXFAIL - failure.  Usually defined to 1.  
 */


/* ------------------------------------------------------------------------ */
/*
 *   Basic memory management interface.  These functions are merely
 *   documented here, but no prototypes are defined, because most
 *   platforms #define macros for these functions and types, mapping them
 *   to malloc or other system interfaces.  
 */

/*
 *   Theoretical maximum osmalloc() size.  This may be less than the
 *   capacity of the argument to osmalloc() on some systems.  For example,
 *   on segmented architectures (such as 16-bit x86), memory is divided into
 *   segments, so a single memory allocation can allocate only a subset of
 *   the total addressable memory in the system.  This value thus specifies
 *   the maximum amount of memory that can be allocated in one chunk.
 *   
 *   Note that this is an architectural maximum for the hardware and
 *   operating system.  It doesn't have anything to do with the total amount
 *   of memory actually available at run-time.
 *   
 *   #define OSMALMAX to a constant long value with theoretical maximum
 *   osmalloc() argument value.  For a platform with a flat (unsegmented)
 *   32-bit memory space, this is usually 0xffffffff; for 16-bit platforms,
 *   this is usually 0xffff.  
 */
/* #define OSMALMAX 0xffffffff */

/*   
 *   Allocate a block of memory of the given size in bytes.  The actual
 *   allocation may be larger, but may be no smaller.  The block returned
 *   should be worst-case aligned (i.e., suitably aligned for any type).
 *   Return null if the given amount of memory is not available.  
 */
/* void *osmalloc(size_t siz); */

/*
 *   Free memory previously allocated with osmalloc().  
 */
/* void osfree(void *block); */

/* 
 *   Reallocate memory previously allocated with osmalloc() or
 *   osrealloc(), changing the block's size to the given number of bytes.
 *   If necessary, a new block at a different address can be allocated, in
 *   which case the data from the original block is copied (the lesser of
 *   the old block size and the new size is copied) to the new block, and
 *   the original block is freed.  If the new size is less than the old
 *   size, this need not do anything at all, since the returned block can
 *   be larger than the new requested size.  If the block cannot be
 *   enlarged to the requested size, return null.  
 */
/* void *osrealloc(void *block, size_t siz); */


/* ------------------------------------------------------------------------ */
/*
 *   Basic file I/O interface.  These functions are merely documented here,
 *   but no prototypes are defined, because most platforms #define macros for
 *   these functions and types, mapping them to stdio or other system I/O
 *   interfaces.  
 *   
 *   When writing a file, writes might or might not be buffered in
 *   application memory; this is up to the OS implementation, which can
 *   perform buffering according to local conventions and what's most
 *   efficient.  However, it shouldn't make any difference to the caller
 *   whether writes are buffered or not - the OS implementation must take
 *   care that any buffering is invisible to the app.  (Porters: note that
 *   the basic C stdio package has the proper behavior here, so you'll get
 *   the correct semantics if you use a simple stdio implementation.)
 *   
 *   Write buffering might be visible to *other* apps, though.  In
 *   particular, another process might not see data written to a file (with
 *   osfwb(), os_fprint(), etc) immediately, since the write functions might
 *   hold the written bytes in an internal memory buffer rather than sending
 *   them to the OS.  Any internal buffers are guaranteed to be flushed to
 *   the OS upon calling osfcls() or osfflush().  Note that it's never
 *   *necessary* to call osfflush(), because buffered data will always be
 *   flushed on closing the file with osfcls().  However, if you want other
 *   apps to be able to see updates immediately, you can use osfflush() to
 *   ensure that buffers are flushed to a file before you close it.
 *   
 *   You can also use osfflush() to check for buffered write errors.  When
 *   you use osfwb() or other write functions to write data, they will return
 *   a success indication even if the data was only copied into a buffer.
 *   This means that a write that appeared to succeed might actually fail
 *   later, when the buffer is flushed.  The only way to know for sure is to
 *   explicitly flush buffers using osfflush(), and check the result code.
 *   If the original write function and a subsequent osfflush() *both* return
 *   success indications, then the write has definitely succeeded.  
 */


/*
 *   Define the following values in your OS header to indicate local
 *   file/path syntax conventions:
 *   
 *   OSFNMAX - integer indicating maximum length of a filename
 *   
 *   OSPATHCHAR - character giving the normal path separator character
 *.  OSPATHALT - string giving other path separator characters
 *.  OSPATHURL - string giving path separator characters for URL conversions
 *.  OSPATHSEP - directory separator for PATH-style environment variables
 *.  OSPATHPWD - string giving the special path representing the current
 *.              working directory; for Unix or Windows, this is "."
 *   
 *   OSPATHURL is a little different: this specifies the characters that
 *   should be converted to URL-style separators when converting a path from
 *   local notation to URL notation.  This is usually the same as the union
 *   of OSPATHCHAR and OSPATHALT, but need not be; for example, on DOS, the
 *   colon (':') is a path separator for most purposes, but is NOT a path
 *   character for URL conversions.
 */

/*
 *   Define the type osfildef as the appropriate file handle structure for
 *   your osfxxx functions.  This type is always used as a pointer, but
 *   the value is always obtained from an osfopxxx call, and is never
 *   synthesized by portable code, so you can use essentially any type
 *   here that you want.
 *   
 *   For platforms that use C stdio functions to implement the osfxxx
 *   functions, osfildef can simply be defined as FILE.
 */
/* typedef FILE osfildef; */


/*
 *   File types.
 *   
 *   These are symbols of the form OSFTxxxx defining various content types,
 *   somewhat aking to MIME types.  These were mainly designed for the old
 *   Mac OS (versions up to OS 9), where the file system stored a type tag
 *   with each file's metadata.  The type tags were used for things like
 *   filtering file selector dialogs and setting file-to-app associations in
 *   the desktop shell.
 *   
 *   Our OSFTxxx symbols are abstract file types that we define, for types
 *   used within the TADS family of applications.  They give us a common,
 *   cross-platform reference point for each type we use.  Each port where
 *   file types are meaningful then maps our abstract type IDs to the
 *   corresponding port-specific type IDs.  In practice, this has never been
 *   used anywhere other than the old Mac OS ports; in fact, it's not even
 *   used in the modern Mac OS (OS X and later), since Apple decided to stop
 *   fighting the tide and start using filename suffixes for this sort of
 *   tagging, like everyone else always has.
 *   
 *   For the list of file types, see osifctyp.h 
 */


/*
 *   Local newline convention.
 *   
 *   Because of the pernicious NIH ("Not Invented Here") cultures of the
 *   major technology vendors, basically every platform out there has its own
 *   unique way of expressing newlines in text files.  Unix uses LF (ASCII
 *   10); Mac uses CR (ASCII 13); DOS and Windows use CR-LF pairs.  In the
 *   past there were heaven-only-knows how many other conventions in use, but
 *   fortunately these three have the market pretty well locked up at this
 *   point.  But we do still have to worry about these three.
 *   
 *   Our strategy on input is to be open to just about anything whenever
 *   possible.  So, when we're reading something that we believe to be a text
 *   file, we'll treat all of these as line endings: CR, LF, CR-LF, and
 *   LF-CR.  It's pretty safe to do this; if we have a CR and LF occurring
 *   adjacently, it's almost certain that they're intended to be taken
 *   together as a single newline sequence.  Likewise, if there's a lone CR
 *   or LF, it's rare for it to mean anything other than a newline.
 *   
 *   On output, though, we can't be as loose.  The problem is that other
 *   applications on our big three platforms *don't* tend to aim for the same
 *   flexibility we do on input: other apps usually expect exactly the local
 *   conventions on input, and don't always work well if they don't get it.
 *   So it's important that when we're writing a text file, we write newlines
 *   in the local convention.  This means that we sometimes need to know what
 *   the local convention actually is.  That's where this definition comes
 *   in.
 *   
 *   Each port must define OS_NEWLINE_SEQ as an ASCII string giving the local
 *   newline sequence to write on output.  For example, DOS defines it as
 *   "\r\n" (CR-LF).  Always define it as a STRING (not a character
 *   constant), even if it's only one character long.
 *   
 *   (Note that some compilers use wacky mappings for \r and \n.  Some older
 *   Mac compilers, for example, defined \n as CR and \r as LF, because of
 *   the Mac convention where newline is represented as CR in a text file.
 *   If there's any such variability on your platform, you can always use the
 *   octal codes to be unambiguous: \012 for LF and \015 for CR.)  
 */
/* #define OS_NEWLINE_SEQ  "\r\n" */



/* 
 *   Open text file for reading.  This opens the file with read-only access;
 *   we're not allowed to write to the file using this handle.  Returns NULL
 *   on error.
 *   
 *   A text file differs from a binary file in that some systems perform
 *   translations to map between C conventions and local file system
 *   conventions; for example, on DOS, the stdio library maps the DOS CR-LF
 *   newline convention to the C-style '\n' newline format.  On many systems
 *   (Unix, for example), there is no distinction between text and binary
 *   files.
 *   
 *   On systems that support file sharing and locking, this should open the
 *   file in "shared read" mode - this means that other processes are allowed
 *   to simultaneously read from the file, but no other processs should be
 *   allowed to write to the file as long as we have it open.  If another
 *   process already has the file open with write access, this routine should
 *   return failure, since we can't take away the write privileges the other
 *   process already has and thus we can't guarantee that other processes
 *   won't write to the file while we have it open.  
 */
/* osfildef *osfoprt(const char *fname, os_filetype_t typ); */

/*
 *   Open a text file for "volatile" reading: we open the file with read-only
 *   access, and we explicitly accept instability in the file's contents due
 *   to other processes simultaneously writing to the file.  On systems that
 *   support file sharing and locking, the file should be opened in "deny
 *   none" mode, meaning that other processes can simultaneously open the
 *   file for reading and/or writing even while have the file open.  
 */
/* osfildef *osfoprtv(const char *fname, os_filetype_t typ); */

/* 
 *   Open text file for writing; returns NULL on error.  If the file already
 *   exists, this truncates the file to zero length, deleting any existing
 *   contents.
 */
/* osfildef *osfopwt(const char *fname, os_filetype_t typ); */

/*
 *   Open text file for reading and writing, keeping the file's existing
 *   contents if the file already exists or creating a new file if no such
 *   file exists.  Returns NULL on error. 
 */
/* osfildef *osfoprwt(const char *fname, os_filetype_t typ); */

/* 
 *   Open text file for reading/writing.  If the file already exists,
 *   truncate the existing contents to zero length.  Create a new file if it
 *   doesn't already exist.  Return null on error.  
 */
/* osfildef *osfoprwtt(const char *fname, os_filetype_t typ); */

/* 
 *   Open binary file for writing; returns NULL on error.  If the file
 *   exists, this truncates the existing contents to zero length.
 */
/* osfildef *osfopwb(const char *fname, os_filetype_t typ); */

/* 
 *   Open source file for reading - use the appropriate text or binary
 *   mode.  
 */
/* osfildef *osfoprs(const char *fname, os_filetype_t typ); */

/* 
 *   Open binary file for reading; returns NULL on error.  
 */
/* osfildef *osfoprb(const char *fname, os_filetype_t typ); */

/*
 *   Open binary file for 'volatile' reading; returns NULL on error.
 *   ("Volatile" means that we'll accept writes from other processes while
 *   reading, so the file should be opened in "deny none" mode or the
 *   equivalent, to the extent that the local system supports file sharing
 *   modes.)  
 */
/* osfildef *osfoprbv(const char *fname, os_filetype_t typ); */

/* 
 *   Open binary file for random-access reading/writing.  If the file already
 *   exists, keep the existing contents; if the file doesn't already exist,
 *   create a new empty file.
 *   
 *   The caller is allowed to perform any mixture of read and write
 *   operations on the returned file handle, and can seek around in the file
 *   to read and write at random locations.
 *   
 *   If the local file system supports file sharing or locking controls, this
 *   should generally open the file in something equivalent to "exclusive
 *   write, shared read" mode ("deny write" in DENY terms), so that other
 *   processes can't modify the file at the same time we're modifying it (but
 *   it doesn't bother us to have other processes reading from the file while
 *   we're working on it, as long as they don't mind that we could change
 *   things on the fly).  It's not absolutely necessary to assert these
 *   locking semantics, but if there's an option to do so this is preferred.
 *   Stricter semantics (such as "exclusive" or "deny all" mode) are better
 *   than less strict semantics.  Less strict semantics are dicey, because in
 *   that case the caller has no way of knowing that another process could be
 *   modifying the file at the same time, and no way (through osifc) of
 *   coordinating that activity.  If less strict semantics are implemented,
 *   the caller will basically be relying on luck to avoid corruptions due to
 *   writing by other processes.
 *   
 *   Return null on error.  
 */
/* osfildef *osfoprwb(const char *fname, os_filetype_t typ); */

/* 
 *   Open binary file for random-access reading/writing.  If the file already
 *   exists, truncate the existing contents (i.e., delete the contents of the
 *   file, resetting it to a zero-length file).  Create a new file if it
 *   doesn't already exist.  The caller is allowed to perform any mixture of
 *   read and write operations on the returned handle, and can seek around in
 *   the file to read and write at random locations.
 *   
 *   The same comments regarding sharing/locking modes for osfoprwb() apply
 *   here as well.
 *   
 *   Return null on error.  
 */
/* osfildef *osfoprwtb(const char *fname, os_filetype_t typ); */

/*
 *   Duplicate a file handle.  Returns a new osfildef* handle that accesses
 *   the same open file as an existing osfildef* handle.  The new handle is
 *   independent of the original handle, with its own seek position,
 *   buffering, etc.  The new handle and the original handle must each be
 *   closed separately when the caller is done with them (closing one doesn't
 *   close the other).  The effect should be roughly the same as the Unix
 *   dup() function.
 *   
 *   On success, returns a new, non-null osfildef* handle duplicating the
 *   original handle.  Returns null on failure.
 *   
 *   'mode' is a simplified stdio fopen() mode string.  The first
 *   character(s) indicate the access type: "r" for read access, "w" for
 *   write access, or "r+" for read/write access.  Note that "w+" mode is
 *   specifically not defined, since the fopen() handling of "w+" is to
 *   truncate any existing file contents, which is not desirable when
 *   duplicating a handle.  The access type can optionally be followed by "t"
 *   for text mode, "s" for source file mode, or "b" for binary mode, with
 *   the same meanings as for the various osfop*() functions.  The default is
 *   't' for text mode if none of these are specified.
 *   
 *   If the osfop*() functions are implemented in terms of stdio FILE*
 *   objects, this can be implemented as fdopen(dup(fileno(orig)), mode), or
 *   using equivalents if the local stdio library uses different names for
 *   these functions.  Note that "s" (source file format) isn't a stdio mode,
 *   so implementations must translate it to the appropriate "t" or "b" mode.
 *   (For that matter, "t" and "b" modes aren't universally supported either,
 *   so some implementations may have to translate these, or more likely
 *   simply remove them, as most platforms don't distinguish text and binary
 *   modes anyway.)
 */
osfildef *osfdup(osfildef *orig, const char *mode);

/* 
 *   Set a file's type information.  This is primarily for implementations on
 *   Mac OS 9 and earlier, where the file system keeps file-type metadata
 *   separate from the filename.  On such systems, this can be used to set
 *   the type metadata after a file is created.  The system should map the
 *   os_filetype_t values to the actual metadata values on the local system.
 *   On most systems, there's no such thing as file-type metadata, in which
 *   case this function should simply be stubbed out with an empty function.
 */
void os_settype(const char *f, os_filetype_t typ);

/* open the error message file for reading */
osfildef *oserrop(const char *arg0);

/* 
 *   Get a line of text from a text file.  Uses fgets semantics.  
 */
/* char *osfgets(char *buf, size_t len, osfildef *fp); */

/* 
 *   Write a line of text to a text file.  Uses fputs semantics.  
 */
/* void osfputs(const char *buf, osfildef *fp); */

/*
 *   Write to a text file.  os_fprintz() takes a null-terminated string,
 *   while os_fprint() takes an explicit separate length argument that might
 *   not end with a null terminator.  
 */
void os_fprintz(osfildef *fp, const char *str);
void os_fprint(osfildef *fp, const char *str, size_t len);

/* 
 *   Write bytes to file.  Return 0 on success, non-zero on error.
 */
/* int osfwb(osfildef *fp, const void *buf, int bufl); */

/*
 *   Flush buffered writes to a file.  This ensures that any bytes written to
 *   the file (with osfwb(), os_fprint(), etc) are actually sent out to the
 *   operating system, rather than being buffered in application memory for
 *   later writing.
 *   
 *   Note that this routine only guarantees that we write through to the
 *   operating system.  This does *not* guarantee that the data will actually
 *   be committed to the underlying physical storage device.  Such a
 *   guarantee is hard to come by in general, since most modern systems use
 *   multiple levels of software and hardware buffering - the OS might buffer
 *   some data in system memory, and the physical disk drive might itself
 *   buffer data in its own internal cache.  This routine thus isn't good
 *   enough, for example, to protect transactional data that needs to survive
 *   a power failure or a serious system crash.  What this routine *does*
 *   ensure is that buffered data are written through to the OS; in
 *   particular, this ensures that another process that's reading from the
 *   same file will see all updates we've made up to this point.
 *   
 *   Returns 0 on success, non-zero on error.  Errors can occur for any
 *   reason that they'd occur on an ordinary write - a full disk, a hardware
 *   failure, etc.  
 */
/* int osfflush(osfildef *fp); */

/* 
 *   Read a character from a file.  Provides the same semantics as fgetc().
 */
/* int osfgetc(osfildef *fp); */

/* 
 *   Read bytes from file.  Return 0 on success, non-zero on error.  
 */
/* int osfrb(osfildef *fp, void *buf, int bufl); */

/* 
 *   Read bytes from file and return the number of bytes read.  0
 *   indicates that no bytes could be read. 
 */
/* size_t osfrbc(osfildef *fp, void *buf, size_t bufl); */

/* 
 *   Get the current seek location in the file.  The first byte of the
 *   file has seek position 0.  
 */
/* long osfpos(osfildef *fp); */

/* 
 *   Seek to a location in the file.  The first byte of the file has seek
 *   position 0.  Returns zero on success, non-zero on error.
 *   
 *   The following constants must be defined in your OS-specific header;
 *   these values are used for the "mode" parameter to indicate where to
 *   seek in the file:
 *   
 *   OSFSK_SET - set position relative to the start of the file
 *.  OSFSK_CUR - set position relative to the current file position
 *.  OSFSK_END - set position relative to the end of the file 
 */
/* int osfseek(osfildef *fp, long pos, int mode); */

/* 
 *   Close a file.
 *   
 *   If the OS implementation uses buffered writes, this routine guarantees
 *   that any buffered data are flushed to the underlying file.  So, it's not
 *   necessary to call osfflush() before calling this routine.  However,
 *   since this function doesn't return any error indication, a caller could
 *   use osfflush() first to check for errors on any final buffered writes.  
 */
/* void osfcls(osfildef *fp); */

/* 
 *   Delete a file.  Returns zero on success, non-zero on error. 
 */
/* int osfdel(const char *fname); */

/*
 *   Rename/move a file.  This should apply the usual C rename() behavior.
 *   Renames the old file to the new name, which may be in a new directory
 *   location if supported on the local system; moves across devices,
 *   volumes, file systems, etc may or may not be supported according to the
 *   local system's rules.  If the new file already exists, results are
 *   undefined.  Returns true on success, false on failure.
 */
/* int os_rename_file(const char *oldname, const char *newname); */


/* ------------------------------------------------------------------------ */
/*
 *   File "stat()" information - mode, size, time stamps 
 */

/* 
 *   Test access to a file - i.e., determine if the file exists.  Returns
 *   zero if the file exists, non-zero if not.  (The semantics may seem
 *   backwards, but this is consistent with the conventions used by most of
 *   the other osfxxx calls: zero indicates success, non-zero indicates an
 *   error.  If the file exists, "accessing" it was successful, so osfacc
 *   returns zero; if the file doesn't exist, accessing it gets an error,
 *   hence a non-zero return code.)  
 */
/* int osfacc(const char *fname) */

/*
 *   Get a file's mode and attribute flags.  This retrieves information on
 *   the given file equivalent to the st_mode member of the 'struct stat'
 *   data returned by the Unix stat() family of functions, as well as some
 *   extra system-specific attributes.  On success, fills in *mode (if mode
 *   is non-null) with the mode information as a bitwise combination of
 *   OSFMODE_xxx values, fills in *attr (if attr is non-null) with a
 *   combination of OSFATTR_xxx attribute flags, and returns true; on
 *   failure, simply returns false.  Failure can occur if the file doesn't
 *   exist, can't be accessed due to permissions, etc.
 *   
 *   Note that 'mode' and/or 'attr' can be null if the caller doesn't need
 *   that information.  Implementations must check these parameters for null
 *   pointers and skip returning the corresponding information if null.
 *   
 *   If the file in 'fname' is a symbolic link, the behavior depends upon
 *   'follow_links'.  If 'follow_links' is true, the function should resolve
 *   the link reference (and if that points to another link, the function
 *   resolves that link as well, and so on) and return information on the
 *   object the link points to.  Otherwise, the function returns information
 *   on the link itself.  This only applies for symbolic links (not for hard
 *   links), and only if the underlying OS and file system support this
 *   distinction; if the OS transparently resolves links and doesn't allow
 *   retrieving information about the link itself, 'follow_links' can be
 *   ignored.  Likewise, hard links (on systems that support them) are
 *   generally indistinguishable from regular files, so this function isn't
 *   expected to do anything special with them.
 *   
 *   The '*mode' value returned is a bitwise combination of OSFMODE_xxx flag.
 *   Many of the flags are mutually exclusive; for example, "file" and
 *   "directory" should never be combined.  It's also possible for '*mode' to
 *   be zero for a valid file; this means that the file is of some special
 *   type on the local system that doesn't fit any of the OSFMODE_xxx types.
 *   (If any ports do encounter such cases, we can add OSFMODE_xxx types to
 *   accommodate new types.  The list below isn't meant to be final; it's
 *   just what we've encountered so far on the platforms where TADS has
 *   already been ported.)
 *   
 *   The OSFMODE_xxx values are left for the OS to define so that they can be
 *   mapped directly to the OS API's equivalent constants, if desired.  This
 *   makes the routine easy to write, since you can simply set *mode directly
 *   to the mode information the OS returns from its stat() or equivalent.
 *   However, note that these MUST be defined as bit flags - that is, each
 *   value must be exactly a power of 2.  Windows and Unix-like systems
 *   follow this practice, as do most "stat()" functions in C run-time
 *   libraries, so this usually works automatically if you map these
 *   constants to OS or C library values.  However, if a port defines its own
 *   values for these, take care that they're all powers of 2.
 *   
 *   Obviously, a given OS might not have all of the file types listed here.
 *   If any OSFMODE_xxx values aren't applicable on the local OS, you can
 *   simply define them as zero since they'll never be returned.
 *   
 *   Notes on attribute flags:
 *   
 *   OSFATTR_HIDDEN means that the file is conventionally hidden by default
 *   in user interface views or listings, but is still fully accessible to
 *   the user.  Hidden files are also usually excluded by default from
 *   wildcard patterns in commands ("rm *.*").  On Unix, a hidden file is one
 *   whose name starts with "."; on Windows, it's a file with the HIDDEN bit
 *   in its file attributes.  On systems where this concept exists, the user
 *   can still manipulate these files as normal by naming them explicitly,
 *   and can typically make them appear in UI views or directory listings via
 *   a preference setting or command flag (e.g., "ls -a" on Unix).  The
 *   "hidden" flag is explicitly NOT a security or permissions mechanism, and
 *   it doesn't protect the file against intentional access by a user; it's
 *   merely a convenience designed to reduce clutter by excluding files
 *   maintained by the OS or by an application (such as preference files,
 *   indices, caches, etc) from casual folder browsing, where a user is
 *   typically only concerned with her own document files.  On systems where
 *   there's no such naming convention or attribute metadata, this flag will
 *   never appear.
 *   
 *   OSFATTR_SYSTEM is similar to 'hidden', but means that the file is
 *   specially marked as an operating system file.  This is mostly a
 *   DOS/Windows concept, where it corresponds to the SYSTEM bit in the file
 *   attributes; this flag will probably never appear on other systems.  The
 *   distinction between 'system' and 'hidden' is somewhat murky even on
 *   Windows; most 'system' file are also marked as 'hidden', and in
 *   practical terms in the user interface, 'system' files are treated the
 *   same as 'hidden'.
 *   
 *   OSFATTR_READ means that the file is readable by this process.
 *   
 *   OSFATTR_WRITE means that the file is writable by this process.
 */
/* int osfmode(const char *fname, int follow_links, */
/*             unsigned long *mode, unsigned long *attr); */

/* file mode/type constants */
/* #define OSFMODE_FILE    - regular file */
/* #define OSFMODE_DIR     - directory */
/* #define OSFMODE_BLK     - block-mode device */
/* #define OSFMODE_CHAR    - character-mode device */
/* #define OSFMODE_PIPE    - pipe/FIFO/other character-oriented IPC */
/* #define OSFMODE_SOCKET  - network socket */
/* #define OSFMODE_LINK    - symbolic link */

/* file attribute constants */
/* #define OSFATTR_HIDDEN  - hidden file */
/* #define OSFATTR_SYSTEM  - system file */
/* #define OSFATTR_READ    - the file is readable by this process */
/* #define OSFATTR_WRITE   - the file is writable by this process */

struct os_file_stat_t {
    /* 
     *   Size of the file, in bytes.  For platforms lacking 64-bit types, we
     *   split this into high and low 32-bit portions.  Platforms where the
     *   native stat() or equivalent only returns a 32-bit file size can
     *   simply set sizehi to zero, since sizelo can hold the entire size
     *   value.
     */
    uint32_t sizelo;
    uint32_t sizehi;

    /* 
     *   Creation time, modification time, and last access time.  If the file
     *   system doesn't keep information on one or more of these, use
     *   (os_time_t)0 to indicate that the timestamp isn't available.  It's
     *   fine to return any subset of these.  Per the standard C stat(),
     *   these should be expressed as seconds after the Unix Epoch.
     */
    os_time_t cre_time;
    os_time_t mod_time;
    os_time_t acc_time;

    /* file mode, using the same flags as returned from osfmode() */
    unsigned long mode;

    /* file attributes, using the same flags as returned from osfmode() */
    unsigned long attrs;
};


/*
 *   Get stat() information.  This fills in the portable os_file_stat
 *   structure with the requested file information.  Returns true on success,
 *   false on failure (file not found, permissions error, etc).
 *   
 *   'follow_links' has the same meaning as for osfmode().
 */
int os_file_stat(const char *fname, int follow_links, os_file_stat_t *s);

/*
 *   Manually resolve a symbolic link.  If the local OS and file system
 *   support symbolic links, and the given filename is a symbolic link (in
 *   which case osfmode(fname, FALSE, &m, &a) will set OSFMODE_LINK in the
 *   mode bits), this fills in 'target' with the name of the link target
 *   (i.e., the object that the link in 'fname' points to).  This should
 *   return a fully qualified file system path.  Returns true on success,
 *   false on failure.
 *   
 *   This should only resolve a single level of indirection.  If the link
 *   target of 'fname' is itself a link to a second target, this should only
 *   resolve the single reference from 'fname' to its direct direct.  Callers
 *   that wish to resolve the final target of a chain of link references must
 *   iterate until the returned path doesn't refer to a link.
 */
int os_resolve_symlink(const char *fname, char *target, size_t target_size);


/* ------------------------------------------------------------------------ */
/*
 *   Get a list of root directories.  If 'buf' is non-null, fills in 'buf'
 *   with a list of strings giving the root directories for the local,
 *   file-oriented devices on the system.  The strings are each null
 *   terminated and are arranged consecutively in the buffer, with an extra
 *   null terminator after the last string to mark the end of the list.
 *   
 *   The return value is the length of the buffer required to hold the
 *   results.  If the caller's buffer is null or is too short, the routine
 *   should return the full length required, and leaves the contents of the
 *   buffer undefined; the caller shouldn't expect any contents to be filled
 *   in if the return value is greater than buflen.  Both 'buflen' and the
 *   return value include the null terminators, including the extra null
 *   terminator at the end of the list.  If an error occurs, or the system
 *   has no concept of a root directory, returns zero.
 *   
 *   Each result string should be expressed using the syntax for the root
 *   directory on a device.  For example, on Windows, "C:\" represents the
 *   root directory on the C: drive.
 *   
 *   "Local" means a device is mounted locally, as opposed to being merely
 *   visible on the network via some remote node syntax; e.g., on Windows
 *   this wouldn't include any UNC-style \\SERVER\SHARE names, and on VMS it
 *   excludes any SERVER:: nodes.  It's up to each system how to treat
 *   virtual local devices, i.e., those that look synctactically like local
 *   devices but are actually mounted network devices, such as Windows mapped
 *   network drives; we recommend including them if it would take extra work
 *   to filter them out, and excluding them if it would take extra work to
 *   include them.  "File-oriented" means that the returned devices are
 *   accessed via file systems, not as character devices or raw block
 *   devices; so this would exclude /dev/xxx devices on Unix and things like
 *   CON: and LPT1: on Windows.
 *   
 *   Examples ("." represents a null byte):
 *   
 *   Windows: C:\.D:\.E:\..
 *   
 *   Unix example: /..
 */
size_t os_get_root_dirs(char *buf, size_t buflen);


/* ------------------------------------------------------------------------ */
/*
 *   Open a directory.  This begins an enumeration of a directory's contents.
 *   'dirname' is a relative or absolute path to a directory.  On success,
 *   returns true, and 'handle' is set to a port-defined handle value that's
 *   used in subsequent calls to os_read_dir() and os_close_dir().  Returns
 *   false on failure.
 *   
 *   If the routine succeeds, the caller must eventually call os_close_dir()
 *   to release the resources associated with the handle.
 */
/* typedef <local system type> osdirhdl_t; */
int os_open_dir(const char *dirname, /*OUT*/osdirhdl_t *handle);

/*
 *   Read the next file in a directory.  'handle' is a handle value obtained
 *   from a call to os_open_dir().  On success, returns true and fills in
 *   'fname' with the next filename; the handle is also internally updated so
 *   that the next call to this function will retrieve the next file, and so
 *   on until all files have been retrieved.  If an error occurs, or there
 *   are no more files in the directory, returns false.
 *   
 *   The filename returned is the root filename only, without the path.  The
 *   caller can build the full path by calling os_build_full_path() or
 *   os_combine_paths() with the original directory name and the returned
 *   filename as parameters.
 *   
 *   This routine lists all objects in the directory that are visible to the
 *   corresponding native API, and is non-recursive.  The listing should thus
 *   include subdirectory objects, but not the contents of subdirectories.
 *   Implementations are encouraged to simply return all objects returned
 *   from the corresponding native directory scan API; there's no need to do
 *   any filtering, except perhaps in cases where it's difficult or
 *   impossible to represent an object in terms of the osifc APIs (e.g., it
 *   might be reasonable to exclude files without names).  System relative
 *   links, such as the Unix/DOS "." and "..", specifically should be
 *   included in the listing.  For unusual objects that don't fit into the
 *   os_file_stat() taxonomy or that otherwise might create confusion for a
 *   caller, err on the side of full disclosure (i.e., just return everything
 *   unfiltered); if necessary, we can extend the os_file_stat() taxonomy or
 *   add new osifc APIs to create a portable abstraction to handle whatever
 *   is unusual or potentially confusing about the native object.  For
 *   example, Unix implementations should feel free to return symbolic link
 *   objects, including dangling links, since we have the portable
 *   os_resolve_symlink() that lets the caller examine the meaning of the
 *   link object.
 */
int os_read_dir(osdirhdl_t handle, char *fname, size_t fname_size);

/*
 *   Close a directory handle.  This releases the resources associated with a
 *   directory search started with os_open_dir().  Every successful call to
 *   os_open_dir() must have a matching call to os_close_dir().  As usual for
 *   open/close protocols, the handle is invalid after calling this function,
 *   so no more calls to os_read_dir() may be made with the handle.
 */
void os_close_dir(osdirhdl_t handle);


/* ------------------------------------------------------------------------ */
/*
 *   NB - this routine is DEPRECATED as of TADS 2.5.16/3.1.1.  Callers should
 *   use os_open_dir(), os_read_dir(), os_close_dir() instead.
 *   
 *   Find the first file matching a given pattern.  The returned context
 *   pointer is a pointer to whatever system-dependent context structure is
 *   needed to continue the search with the next file, and is opaque to the
 *   caller.  The caller must pass the context pointer to the next-file
 *   routine.  The caller can optionally cancel a search by calling the
 *   close-search routine with the context pointer.  If the return value is
 *   null, it indicates that no matching files were found.  If a file was
 *   found, outbuf will be filled in with its name, and isdir will be set to
 *   true if the match is a directory, false if it's a file.  If pattern is
 *   null, all files in the given directory should be returned; otherwise,
 *   pattern is a string containing '*' and '?' as wildcard characters, but
 *   not containing any directory separators, and all files in the given
 *   directory matching the pattern should be returned.
 *   
 *   Important: because this routine may allocate memory for the returned
 *   context structure, the caller must either call os_find_next_file until
 *   that routine returns null, or call os_find_close() to cancel the search,
 *   to ensure that the os code has a chance to release the allocated memory.
 *   
 *   'outbuf' should be set on output to the name of the matching file,
 *   without any path information.
 *   
 *   'outpathbuf' should be set on output to full path of the matching file.
 *   If possible, 'outpathbuf' should use the same relative or absolute
 *   notation that the search criteria used on input.  For example, if dir =
 *   "resfiles", and the file found is "MyPic.jpg", outpathbuf should be set
 *   to "resfiles/MyPic.jpg" (or appropriate syntax for the local platform).
 *   Similarly, if dir = "/home/tads/resfiles", outpath buf should be
 *   "/home/tads/resfiles/MyPic.jpg".  The result should always conform to
 *   correct local conventions, which may require some amount of manipulation
 *   of the filename; for example, on the Mac, if dir = "resfiles", the
 *   result should be ":resfiles:MyPic.jpg" (note the added leading colon) to
 *   conform to Macintosh relative path notation.
 *   
 *   Note that 'outpathbuf' may be null, in which case the caller is not
 *   interested in the full path information.  
 */
/*   
 *   Note the following possible ways this function may be called:
 *   
 *   dir = "", pattern = filename - in this case, pattern is the name of a
 *   file or directory in the current directory.  filename *might* be a
 *   relative path specified by the user (on a command line, for example);
 *   for instance, on Unix, it could be something like "resfiles/jpegs".
 *   
 *   dir = path, pattern = filname - same as above, but this time the
 *   filename or directory pattern is relative to the given path, rather
 *   than to the current directory.  For example, we could have dir =
 *   "/games/mygame" and pattern = "resfiles/jpegs".
 *   
 *   dir = path, pattern = 0 (NULL) - this should search for all files in
 *   the given path.  The path might be absolute or it might be relative.
 *   
 *   dir = path, pattern = "*" - this should have the same result as when
 *   pattern = 0.
 *   
 *   dir = path, pattern = "*.ext" - this should search for all files in
 *   the given path whose names end with ".ext".
 *   
 *   dir = path, pattern = "abc*" - this should search for all files in
 *   the given path whose names start with "abc".
 *   
 *   All of these combinations are possible because callers, for
 *   portability, must generally not manipulate filenames directly;
 *   instead, callers obtain paths and search strings from external
 *   sources, such as from the user, and present them to this routine with
 *   minimal manipulation.  
 */
void *os_find_first_file(const char *dir,
                         char *outbuf, size_t outbufsiz, int *isdir,
                         char *outpathbuf, size_t outpathbufsiz);

/*
 *   Implementation notes for porting os_find_first_file:
 *   
 *   The algorithm for this routine should go something like this:
 *   
 *   - If 'path' is null, create a variable real_path and initialize it
 *   with the current directory.  Otherwise, copy path to real_path.
 *   
 *   - If 'pattern' contains any directory separators ("/" on Unix, for
 *   example), change real_path so that it reflects the additional leading
 *   subdirectories in the path in 'pattern', and remove the leading path
 *   information from 'pattern'.  For example, on Unix, if real_path
 *   starts out as "./subdir", and pattern is "resfiles/jpegs", change
 *   real_path to "./subdir/resfiles", and change pattern to "jpegs".
 *   Take care to add and remove path separators as needed to keep the
 *   path strings well-formed.
 *   
 *   - Begin a search using appropriate OS API's for all files in
 *   real_path.
 *   
 *   - Check each file found.  Skip any files that don't match 'pattern',
 *   treating "*" as a wildcard that matches any string of zero or more
 *   characters, and "?" as a wildcard that matches any single character
 *   (or matches nothing at the end of a string).  For example:
 *   
 *.  "*" matches anything
 *.  "abc?" matches "abc", "abcd", "abce", "abcf", but not "abcde"
 *.  "abc???" matches "abc", "abcd", "abcde", "abcdef", but not "abcdefg"
 *.  "?xyz" matches "wxyz", "axyz", but not "xyz" or "abcxyz"
 *   
 *   - Return the first file that matches, if any, by filling in 'outbuf'
 *   and 'isdir' with appropriate information.  Before returning, allocate
 *   a context structure (which is entirely for your own use, and opaque
 *   to the caller) and fill it in with the information necessary for
 *   os_find_next_file to get the next matching file.  If no file matches,
 *   return null.  
 */


/*
 *   Find the next matching file, continuing a search started with
 *   os_find_first_file().  Returns null if no more files were found, in
 *   which case the search will have been automatically closed (i.e.,
 *   there's no need to call os_find_close() after this routine returns
 *   null).  Returns a non-null context pointer, which is to be passed to
 *   this function again to get the next file, if a file was found.
 *   
 *   'outbuf' and 'outpathbuf' are filled in with the filename (without
 *   path) and full path (relative or absolute, as appropriate),
 *   respectively, in the same manner as they do for os_find_first_file().
 *   
 *   Implementation note: if os_find_first_file() allocated memory for the
 *   search context, this routine must free the memory if it returs null,
 *   because this indicates that the search is finished and the caller
 *   need not call os_find_close().  
 */
void *os_find_next_file(void *ctx, char *outbuf, size_t outbufsiz,
                        int *isdir, char *outpathbuf, size_t outpathbufsiz);

/*
 *   Cancel a search.  The context pointer returned by the last call to
 *   os_find_first_file() or os_find_next_file() is the parameter.  There
 *   is no need to call this function if find-first or find-next returned
 *   null, since they will have automatically closed the search.
 *   
 *   Implementation note: if os_find_first_file() allocated memory for the
 *   search context, this routine should release the memory.  
 */
void os_find_close(void *ctx);

/*
 *   Special filename classification 
 */
enum os_specfile_t
{
    /* not a special file */
    OS_SPECFILE_NONE,

    /* 
     *   current directory link - this is a file like the "." file on Unix
     *   or DOS, which is a special link that simply refers to itself 
     */
    OS_SPECFILE_SELF,

    /* 
     *   parent directory link - this is a file like the ".." file on Unix
     *   or DOS, which is a special link that refers to the parent
     *   directory 
     */
    OS_SPECFILE_PARENT
};

/*
 *   Determine if the given filename refers to a special file.  Returns the
 *   appropriate enum value if so, or OS_SPECFILE_NONE if not.  The given
 *   filename must be a root name - it must not contain a path prefix.  The
 *   purpose here is to classify the results from os_find_first_file() and
 *   os_find_next_file() to identify the special relative links, so callers
 *   can avoid infinite recursion when traversing a directory tree.
 */
enum os_specfile_t os_is_special_file(const char *fname);

/* ------------------------------------------------------------------------ */
/* 
 *   Convert string to all-lowercase. 
 */
char *os_strlwr(char *s);


/* ------------------------------------------------------------------------ */
/*
 *   Character classifications for quote characters.  os_squote() returns
 *   true if its argument is any type of single-quote character;
 *   os_dquote() returns true if its argument is any type of double-quote
 *   character; and os_qmatch(a, b) returns true if a and b are matching
 *   open- and close-quote characters.
 *   
 *   These functions allow systems with extended character codes with
 *   weird quote characters (such as the Mac) to match the weird
 *   characters, so that users can use the extended quotes in input.
 *   
 *   These are usually implemented as macros.  The most common
 *   implementation simply returns true for the standard ASCII quote
 *   characters:
 *   
 *   #define os_squote(c) ((c) == '\'')
 *.  #define os_dquote(c) ((c) == '"')
 *.  #define os_qmatch(a, b) ((a) == (b))
 *   
 *   These functions take int arguments to allow for the possibility of
 *   Unicode input.  
 */
/* int os_squote(int c); */
/* int os_dquote(int c); */
/* int os_qmatch(int a, int b); */


/* ------------------------------------------------------------------------ */
/*
 *   Special file and directory locations
 */

/*
 *   Get the full filename (including directory path) to the executable
 *   file, given the argv[0] parameter passed into the main program.  This
 *   fills in the buffer with a null-terminated string that can be used in
 *   osfoprb(), for example, to open the executable file.
 *   
 *   Returns non-zero on success.  If it's not possible to determine the
 *   name of the executable file, returns zero.
 *   
 *   Some operating systems might not provide access to the executable file
 *   information, so non-trivial implementation of this routine is optional;
 *   if the necessary information is not available, simply implement this to
 *   return zero.  If the information is not available, callers should offer
 *   gracefully degraded functionality if possible.  
 */
int os_get_exe_filename(char *buf, size_t buflen, const char *argv0);

/*
 *   Get a special directory path.  Returns the selected path, in a format
 *   suitable for use with os_build_full_path().  The main program's argv[0]
 *   parameter is provided so that the system code can choose to make the
 *   special paths relative to the program install directory, but this is
 *   entirely up to the system implementation, so the argv[0] parameter can
 *   be ignored if it is not needed.
 *   
 *   The 'id' parameter selects which special path is requested; this is one
 *   of the constants defined below.  If the id is not understood, there is
 *   no way of signalling an error to the caller; this routine can fail with
 *   an assert() in such cases, because it indicates that the OS layer code
 *   is out of date with respect to the calling code.
 *   
 *   This routine can be implemented using one of the strategies below, or a
 *   combination of these.  These are merely suggestions, though, and systems
 *   are free to ignore these and implement this routine using whatever
 *   scheme is the best fit to local conventions.
 *   
 *   - Relative to argv[0].  Some systems use this approach because it keeps
 *   all of the TADS files together in a single install directory tree, and
 *   doesn't require any extra configuration information to find the install
 *   directory.  Since we base the path name on the executable that's
 *   actually running, we don't need any environment variables or parameter
 *   files or registry entries to know where to look for related files.
 *   
 *   - Environment variables or local equivalent.  On some systems, it is
 *   conventional to set some form of global system parameter (environment
 *   variables on Unix, for example) for this sort of install configuration
 *   data.  In these cases, this routine can look up the appropriate
 *   configuration variables in the system environment.
 *   
 *   - Hard-coded paths.  Some systems have universal conventions for the
 *   installation configuration of compiler-like tools, so the paths to our
 *   component files can be hard-coded based on these conventions.
 *   
 *   - Hard-coded default paths with environment variable overrides.  Let the
 *   user set environment variables if they want, but use the standard system
 *   paths as hard-coded defaults if the variables aren't set.  This is often
 *   the best choice; users who expect the standard system conventions won't
 *   have to fuss with any manual settings or even be aware of them, while
 *   users who need custom settings aren't stuck with the defaults.
 */
void os_get_special_path(char *buf, size_t buflen,
                         const char *argv0, int id);

/* 
 *   TADS 3 system resource path.  This path is used to load system
 *   resources, such as character mapping files and error message files.  
 */
#define OS_GSP_T3_RES       1

/* 
 *   TADS 3 compiler - system headers.  This is the #include path for the
 *   header files included with the compiler. 
 */
#define OS_GSP_T3_INC       2

/*
 *   TADS 3 compiler - system library source code.  This is the path to the
 *   library source files that the compiler includes in every compilation by
 *   default (such as _main.t). 
 */
#define OS_GSP_T3_LIB       3

/*
 *   TADS 3 compiler - user library path list.  This is a list of directory
 *   paths, separated by the OSPATHSEP character, that should be searched for
 *   user library files.  The TADS 3 compiler uses this as an additional set
 *   of locations to search after the list of "-Fs" options and before the
 *   OS_GSP_T3_LIB directory.
 *   
 *   This path list is intended for the user's use, so no default value is
 *   needed.  The value should be user-configurable using local conventions;
 *   on Unix, for example, this might be handled with an environment
 *   variable.  
 */
#define OS_GSP_T3_USER_LIBS 4

/*
 *   TADS 3 interpreter - application data path.  This is the directory where
 *   we should store things like option settings: data that we want to store
 *   in a file, global to all games.  Depending on local system conventions,
 *   this can be a global shared directory for all users, or can be a
 *   user-specific directory. 
 */
#define OS_GSP_T3_APP_DATA 5

/*
 *   TADS 3 interpreter - system configuration files.  This is used for files
 *   that affect all games, and generally all users on the system, so it
 *   should be in a central location.  On Windows, for example, we simply
 *   store these files in the install directory containing the intepreter
 *   binary.  
 */
#define OS_GSP_T3_SYSCONFIG  6

/*
 *   System log files.  This is the directory for system-level status, debug,
 *   and error logging files.  (Note that we're NOT talking about in-game
 *   transcript logging per the SCRIPT command.  SCRIPT logs are usually sent
 *   to files selected by the user via a save-file dialog, so these don't
 *   need a special location.)
 */
#define OS_GSP_LOGFILE  7


/* 
 *   Seek to the resource file embedded in the current executable file,
 *   given the main program's argv[0].
 *   
 *   On platforms where the executable file format allows additional
 *   information to be attached to an executable, this function can be used
 *   to find the extra information within the executable.
 *   
 *   The 'typ' argument gives a resource type to find.  This is an arbitrary
 *   string that the caller uses to identify what type of object to find.
 *   The "TGAM" type, for example, is used by convention to indicate a TADS
 *   compiled GAM file.  
 */
osfildef *os_exeseek(const char *argv0, const char *typ);


/* ------------------------------------------------------------------------ */
/*
 *   Load a string resource.  Given a string ID number, load the string
 *   into the given buffer.
 *   
 *   Returns zero on success, non-zero if an error occurs (for example,
 *   the buffer is too small, or the requested resource isn't present).
 *   
 *   Whenever possible, implementations should use an operating system
 *   mechanism for loading the string from a user-modifiable resource
 *   store; this will make localization of these strings easier, since the
 *   resource store can be modified without the need to recompile the
 *   application.  For example, on the Macintosh, the normal system string
 *   resource mechanism should be used to load the string from the
 *   application's resource fork.
 *   
 *   When no operating system mechanism exists, the resources can be
 *   stored as an array of strings in a static variable; this isn't ideal,
 *   because it makes it much more difficult to localize the application.
 *   
 *   Resource ID's are application-defined.  For example, for TADS 2,
 *   "res.h" defines the resource ID's.  
 */
int os_get_str_rsc(int id, char *buf, size_t buflen);


/* ------------------------------------------------------------------------ */
/*
 *   Look for a file in the "standard locations": current directory, program
 *   directory, PATH-like environment variables, etc.  The actual standard
 *   locations are specific to each platform; the implementation is free to
 *   use whatever conventions are appropriate to the local system.  On
 *   systems that have something like Unix environment variables, it might be
 *   desirable to define a TADS-specific variable (TADSPATH, for example)
 *   that provides a list of directories to search for TADS-related files.
 *   
 *   On return, fill in 'buf' with the full filename of the located copy of
 *   the file (if a copy was indeed found), in a format suitable for use with
 *   the osfopxxx() functions; in other words, after this function returns,
 *   the caller should be able to pass the contents of 'buf' to an osfopxxx()
 *   function to open the located file.
 *   
 *   Returns true (non-zero) if a copy of the file was located, false (zero)
 *   if the file could not be found in any of the standard locations.  
 */
int os_locate(const char *fname, int flen, const char *arg0,
              char *buf, size_t bufsiz);


/* ------------------------------------------------------------------------ */
/*
 *   Create and open a temporary file.  The file must be opened to allow
 *   both reading and writing, and must be in "binary" mode rather than
 *   "text" mode, if the system makes such a distinction.  Returns null on
 *   failure.
 *   
 *   If 'fname' is non-null, then this routine should create and open a file
 *   with the given name.  When 'fname' is non-null, this routine does NOT
 *   need to store anything in 'buf'.  Note that the routine shouldn't try
 *   to put the file in a special directory or anything like that; just open
 *   the file with the name exactly as given.
 *   
 *   If 'fname' is null, this routine must choose a file name and fill in
 *   'buf' with the chosen name; if possible, the file should be in the
 *   conventional location for temporary files on this system, and should be
 *   unique (i.e., it shouldn't be the same as any existing file).  The
 *   filename stored in 'buf' is opaque to the caller, and cannot be used by
 *   the caller except to pass to osfdel_temp().  On some systems, it may
 *   not be possible to determine the actual filename of a temporary file;
 *   in such cases, the implementation may simply store an empty string in
 *   the buffer.  (The only way the filename would be unavailable is if the
 *   implementation uses a system API that creates a temporary file, and
 *   that API doesn't return the name of the created temporary file.  In
 *   such cases, we don't need the name; the only reason we need the name is
 *   so we can pass it to osfdel_temp() later, but since the system is going
 *   to delete the file automatically, osfdel_temp() doesn't need to do
 *   anything and thus doesn't need the name.)
 *   
 *   After the caller is done with the file, it should close the file (using
 *   osfcls() as normal), then the caller MUST call osfdel_temp() to delete
 *   the temporary file.
 *   
 *   This interface is intended to take advantage of systems that have
 *   automatic support for temporary files, while allowing implementation on
 *   systems that don't have any special temp file support.  On systems that
 *   do have automatic delete-on-close support, this routine should use that
 *   system-level support, because it helps ensure that temp files will be
 *   deleted even if the caller fails to call osfdel_temp() due to a
 *   programming error or due to a process or system crash.  On systems that
 *   don't have any automatic delete-on-close support, this routine can
 *   simply use the same underlying system API that osfoprwbt() normally
 *   uses (although this routine must also generate a name for the temp file
 *   when the caller doesn't supply one).
 *   
 *   This routine can be implemented using ANSI library functions as
 *   follows: if 'fname' is non-null, return fopen(fname,"w+b"); otherwise,
 *   set buf[0] to '\0' and return tmpfile().  
 */
osfildef *os_create_tempfile(const char *fname, char *buf);

/*
 *   Delete a temporary file - this is used to delete a file created with
 *   os_create_tempfile().  For most platforms, this can simply be defined
 *   the same way as osfdel().  For platforms where the operating system or
 *   file manager will automatically delete a file opened as a temporary
 *   file, this routine should do nothing at all, since the system will take
 *   care of deleting the temp file.
 *   
 *   Callers are REQUIRED to call this routine after closing a file opened
 *   with os_create_tempfile().  When os_create_tempfile() is called with a
 *   non-null 'fname' argument, the same value should be passed as 'fname' to
 *   this function.  When os_create_tempfile() is called with a null 'fname'
 *   argument, then the buffer passed in the 'buf' argument to
 *   os_create_tempfile() must be passed as the 'fname' argument here.  In
 *   other words, if the caller explicitly names the temporary file to be
 *   opened in os_create_tempfile(), then that same filename must be passed
 *   here to delete the named file; if the caller lets os_create_tempfile()
 *   generate a filename, then the generated filename must be passed to this
 *   routine.
 *   
 *   If os_create_tempfile() is implemented using ANSI library functions as
 *   described above, then this routine can also be implemented with ANSI
 *   library calls as follows: if 'fname' is non-null and fname[0] != '\0',
 *   then call remove(fname); otherwise do nothing.  
 */
int osfdel_temp(const char *fname);

/*
 *   Get the temporary file path.  This should fill in the buffer with a
 *   path prefix (suitable for strcat'ing a filename onto) for a good
 *   directory for a temporary file, such as the swap file.  
 */
void os_get_tmp_path(char *buf);

/* 
 *   Generate a name for a temporary file.  This constructs a random file
 *   path in the system temp directory that isn't already used by an existing
 *   file.
 *   
 *   On systems with long filenames, this can be implemented by selecting a
 *   GUID-strength random name (such as 32 random hex digits) with a decent
 *   random number generator.  That's long enough that the odds of a
 *   collision are essentially zero.  On systems that only support short
 *   filenames, the odds of a collision are non-zero, so the routine should
 *   actually check that the chosen filename doesn't exist.
 *   
 *   Optionally, before returning, this routine *may* create (and close) an
 *   empty placeholder file to "reserve" the chosen filename.  This isn't
 *   required, and on systems with long filenames it's usually not necessary
 *   because of the negligible chance of a collision.  On systems with short
 *   filenames, a placeholder can be useful to prevent a subsequent call to
 *   this routine, or a separate process, from using the same filename before
 *   the caller has had a chance to use the returned name to create the
 *   actual temp file.
 *   
 *   Returns true on success, false on failure.  This can fail if there's no
 *   system temporary directory defined, or the temp directory is so full of
 *   other files that we can't find an unused filename.  
 */
int os_gen_temp_filename(char *buf, size_t buflen);


/* ------------------------------------------------------------------------ */
/*
 *   Basic directory/folder management routines
 */

/*
 *   Switch to a new working directory.
 *   
 *   This is meant to behave similarly to the Unix concept of a working
 *   directory, in that it sets the base directory assumed for subsequent
 *   file operations (e.g., the osfopxx() functions, osfdel(), etc - anything
 *   that takes a filename or directory name as an argument).  The working
 *   directory applies to filenames specified with relative paths in the
 *   local system notation.  File operations on filenames specified with
 *   absolute paths, of course, ignore the working directory.
 */
void os_set_pwd(const char *dir);

/*
 *   Switch the working directory to the directory containing the given
 *   file.  Generally, this routine should only need to parse the filename
 *   enough to determine the part that's the directory path, then use
 *   os_set_pwd() to switch to that directory.  
 */
void os_set_pwd_file(const char *filename);

/*
 *   Create a directory.  This creates a new directory/folder with the given
 *   name, which may be given as a relative or absolute path.  Returns true
 *   on success, false on failure.
 *   
 *   If 'create_parents' is true, and the directory has mulitiple path
 *   elements, this routine should create each enclosing parent that doesn't
 *   already exist.  For example, if the path is specified as "a/b/c", and
 *   there exists a folder "a" in the working directory, but "a" is empty,
 *   this should first create "b" and then create "c".  If an error occurs
 *   creating any parent, the routine should simply stop and return failure.
 *   (Optionally, the routine may attempt to behave atomically by undoing any
 *   parent folder creations it accomplished before failing on a nested
 *   folder, but this isn't required.  To reduce the chances of a failure
 *   midway through the operation, the routine might want to scan the
 *   filename before starting to ensure that it contains only valid
 *   characters, since an invalid character is the most likely reason for a
 *   failure part of the way through.)
 *   
 *   We recommend making the routine flexible in terms of the notation it
 *   accepts; e.g., on Unix, "/dir/sub/folder" and "/dir/sub/folder/" should
 *   be considered equivalent.
 */
int os_mkdir(const char *dir, int create_parents);

/*
 *   Remove a directory.  Returns true on success, false on failure.
 *   
 *   If the directory isn't already empty, this routine fails.  That is, the
 *   routine does NOT recursively delete the contents of a non-empty
 *   directory.  It's up to the caller to delete any contents before removing
 *   the directory, if that's the caller's intention.  (Note to implementors:
 *   most native OS APIs to remove directories fail by default if the
 *   directory isn't empty, so it's usually safe to implement this simply by
 *   calling the native API.  However, if your system's version of this API
 *   can remove a non-empty directory, you MUST add an extra test before
 *   removing the directory to ensure it's empty, and return failure if it's
 *   not.  For the purposes of this test, "empty" should of course ignore any
 *   special objects that are automatically or implicitly present in all
 *   directories, such as the Unix "." and ".." relative links.)
 */
int os_rmdir(const char *dir);


/* ------------------------------------------------------------------------ */
/*
 *   Filename manipulation routines
 */

/* apply a default extension to a filename, if it doesn't already have one */
void os_defext(char *fname, const char *ext);

/* unconditionally add an extention to a filename */
void os_addext(char *fname, const char *ext);

/* remove the extension from a filename */
void os_remext(char *fname);

/*
 *   Compare two file names/paths for syntactic equivalence.  Returns true if
 *   the names are equivalent names according to the local file system's
 *   syntax conventions, false if not.  This does a syntax-only comparison of
 *   the paths, without looking anything up in the file system.  This means
 *   that a false return doesn't guarantee that the paths don't point to the
 *   same file.
 *   
 *   This routine DOES make the following equivalences:
 *   
 *   - if the local file system is insensitive to case, the names are
 *   compared ignoring case
 *   
 *   - meaningless path separator difference are ignored: on Unix, "a/b" ==
 *   "a//b" == "a/b/"; on Windows, "a/b" == "a\\b"
 *   
 *   - relative links that are strictly structural or syntactic are applied;
 *   for example, on Unix or Windows, "a/./b" == "a/b" = "a/b/c/..".  This
 *   only applies for special relative links that can be resolved without
 *   looking anything up in the file system.
 *   
 *   This DOES NOT do the following:
 *   
 *   - it doesn't apply working directories/volums to relative paths
 *   
 *   - it doesn't follow symbolic links in the file system
 */
int os_file_names_equal(const char *a, const char *b);

/*
 *   Get a pointer to the root name portion of a filename.  This is the part
 *   of the filename after any path or directory prefix.  For example, on
 *   Unix, given the string "/home/mjr/deep.gam", this function should return
 *   a pointer to the 'd' in "deep.gam".  If the filename doesn't appear to
 *   have a path prefix, it should simply return the argument unchanged.
 *   
 *   IMPORTANT: the returned pointer MUST point into the original 'buf'
 *   string, and the contents of that buffer must NOT be modified.  The
 *   return value must point into the same buffer because there are no
 *   allowances for the alternatives.  In particular, (a) you can't return a
 *   pointer to newly allocated memory, because callers won't free it, so
 *   doing so would cause a memory leak; and (b) you can't return a pointer
 *   to an internal static buffer, because callers might call this function
 *   more than once and still rely on a value returned on an older call,
 *   which would be invalid if a static buffer could be overwritten on each
 *   call.  For these reasons, it's required that the return value point to a
 *   position within the original string passed in 'buf'.  
 */
char *os_get_root_name(const char *buf);

/*
 *   Determine whether a filename specifies an absolute or relative path.
 *   This is used to analyze filenames provided by the user (for example,
 *   in a #include directive, or on a command line) to determine if the
 *   filename can be considered relative or absolute.  This can be used,
 *   for example, to determine whether to search a directory path for a
 *   file; if a given filename is absolute, a path search makes no sense.
 *   A filename that doesn't specify an absolute path can be combined with
 *   a path using os_build_full_path().
 *   
 *   Returns true if the filename specifies an absolute path, false if
 *   not.  
 */
int os_is_file_absolute(const char *fname);

/*
 *   Extract the path from a filename.  Fills in pathbuf with the path
 *   portion of the filename.  If the filename has no path, the pathbuf
 *   should be set appropriately for the current directory (on Unix or DOS,
 *   for example, it can be set to an empty string).
 *   
 *   The result can end with a path separator character or not, depending on
 *   local OS conventions.  Paths extracted with this function can only be
 *   used with os_build_full_path(), so the conventions should match that
 *   function's.
 *   
 *   Unix examples:
 *   
 *.   /home/mjr/deep.gam -> /home/mjr
 *.   games/deep.gam -> games
 *.   deep.gam -> [empty string]
 *   
 *   Mac examples:
 *   
 *    :home:mjr:deep.gam -> :home:mjr
 *.   Hard Disk:games:deep.gam -> Hard Disk:games
 *.   Hard Disk:deep.gam -> Hard Disk:
 *.   deep.gam -> [empty string]
 *   
 *   VMS examples:
 *
 *.   SYS$DISK:[mjr.games]deep.gam -> SYS$DISK:[mjr.games]
 *.   SYS$DISK:[mjr.games] -> SYS$DISK:[mjr]
 *.   deep.gam -> [empty string]
 *   
 *   Note in the last example that we've retained the trailing colon in the
 *   path, whereas we didn't in the others; although the others could also
 *   retain the trailing colon, it's required only for the last case.  The
 *   last case requires the colon because it would otherwise be impossible to
 *   determine whether "Hard Disk" was a local subdirectory or a volume name.
 *   
 */
void os_get_path_name(char *pathbuf, size_t pathbuflen, const char *fname);

/*
 *   Build a full path name, given a path and a filename.  The path may have
 *   been specified by the user, or may have been extracted from another file
 *   via os_get_path_name().  This routine must take care to add path
 *   separators as needed, but also must take care not to add too many path
 *   separators.
 *   
 *   This routine should reformat the path into canonical format to the
 *   extent possible purely through syntactic analysis.  For example, special
 *   relative links, such as Unix "." and "..", should be resolved; for
 *   example, combining "a/./b/c" with ".." on Unix should yield "a/b".
 *   However, symbolic links that require looking up names in the file system
 *   should NOT be resolved.  We don't want to perform any actual file system
 *   lookups because might want to construct hypothetical paths that don't
 *   necessarily relate to files on the local system.
 *   
 *   Note that relative path names may require special care on some
 *   platforms.  In particular, if the source path is relative, the result
 *   should also be relative.  For example, on the Macintosh, a path of
 *   "games" and a filename "deep.gam" should yield ":games:deep.gam" - note
 *   the addition of the leading colon to make the result path relative.
 *   
 *   Note also that the 'filename' argument is not only allowed to be an
 *   ordinary file, possibly qualified with a relative path, but is also
 *   allowed to be a subdirectory.  The result in this case should be a path
 *   that can be used as the 'path' argument to a subsequent call to
 *   os_build_full_path; this allows a path to be built in multiple steps by
 *   descending into subdirectories one at a time.
 *   
 *   Unix examples:
 *   
 *.   /home/mjr + deep.gam -> /home/mjr/deep.gam"
 *.   /home/mjr + .. -> /home
 *.   /home/mjr + ../deep.gam -> /home/deep.gam
 *.   /home/mjr/ + deep.gam -> /home/mjr/deep.gam"
 *.   games + deep.gam -> games/deep.gam"
 *.   games/ + deep.gam -> games/deep.gam"
 *.   /home/mjr + games/deep.gam -> /home/mjr/games/deep.gam"
 *.   games + scifi/deep.gam -> games/scifi/deep.gam"
 *.   /home/mjr + games -> /home/mjr/games"
 *   
 *   Mac examples:
 *   
 *.   Hard Disk: + deep.gam -> Hard Disk:deep.gam
 *.   :games: + deep.gam -> :games:deep.gam
 *.   :games:deep + ::test.gam -> :games:test.gam
 *.   games + deep.gam -> :games:deep.gam
 *.   Hard Disk: + :games:deep.gam -> Hard Disk:games:deep.gam
 *.   games + :scifi:deep.gam -> :games:scifi:deep.gam
 *.   Hard Disk: + games -> Hard Disk:games
 *.   Hard Disk:games + scifi -> Hard Disk:games:scifi
 *.   Hard Disk:games:scifi + deep.gam -> Hard Disk:games:scifi:deep.gam
 *.   Hard Disk:games + :scifi:deep.gam -> Hard Disk:games:scifi:deep.gam
 *   
 *   VMS examples:
 *   
 *.   [home.mjr] + deep.gam -> [home.mjr]deep.gam
 *.   [home.mjr] + [-]deep.gam -> [home]deep.gam
 *.   mjr.dir + deep.gam -> [.mjr]deep.gam
 *.   [home]mjr.dir + deep.gam -> [home.mjr]deep.gam
 *.   [home] + [.mjr]deep.gam -> [home.mjr]deep.gam
 */
void os_build_full_path(char *fullpathbuf, size_t fullpathbuflen,
                        const char *path, const char *filename);

/*
 *   Combine a path and a filename to form a full path to the file.  This is
 *   *almost* the same as os_build_full_path(), but if the 'filename' element
 *   is a special relative link, such as Unix '.' or '..', this preserves
 *   that special link in the final name.
 *   
 *   Unix examples:
 *   
 *.    /home/mjr + deep.gam -> /home/mjr/deep.gam
 *.    /home/mjr + . -> /home/mjr/.
 *.    /home/mjr + .. -> /home/mjr/..
 *   
 *   Mac examples:
 *   
 *.    Hard Disk:games + deep.gam -> HardDisk:games:deep.gam
 *.    Hard Disk:games + :: -> HardDisk:games::
 *   
 *   VMS exmaples:
 *   
 *.    [home.mjr] + deep.gam -> [home.mjr]deep.gam
 *.    [home.mjr] + [-] -> [home.mjr.-]
 */
void os_combine_paths(char *fullpathbuf, size_t pathbuflen,
                      const char *path, const char *filename);


/*
 *   Get the absolute, fully qualified filename for a file.  This fills in
 *   'result_buf' with the absolute path to the given file, taking into
 *   account the current working directory and any other implied environment
 *   information that affects the way the file system would resolve the given
 *   file name to a specific file on disk if we opened the file now using
 *   this name.
 *   
 *   The returned path should be in absolute path form, meaning that it's
 *   independent of the current working directory or any other environment
 *   settings.  That is, this path should still refer to the same file even
 *   if the working directory changes.
 *   
 *   Note that it's valid to get the absolute path for a file that doesn't
 *   exist, or for a path with directory components that don't exist.  For
 *   example, a caller might generate the absolute path for a file that it's
 *   about to create, or a hypothetical filename for path comparison
 *   purposes.  The function should succeed even if the file or any path
 *   components don't exist.  If the file is in relative format, and any path
 *   elements don't exist but are syntactically well-formed, the result
 *   should be the path obtained from syntactically combining the working
 *   directory with the relative path.
 *   
 *   On many systems, a given file might be reachable through more than one
 *   absolute path.  For example, on Unix it might be possible to reach a
 *   file through symbolic links to the file itself or to parent directories,
 *   or hard links to the file.  It's up to the implementation to determine
 *   which path to use in such cases.
 *   
 *   On success, returns true.  If it's not possible to resolve the file name
 *   to an absolute path, the routine copies the original filename to the
 *   result buffer exactly as given, and returns false.  
 */
int os_get_abs_filename(char *result_buf, size_t result_buf_size,
                        const char *filename);

/*
 *   Get the relative version of the given filename path 'filename', relative
 *   to the given base directory 'basepath'.  Both paths must be given in
 *   absolute format.
 *   
 *   Returns true on success, false if it's not possible to rewrite the path
 *   in relative terms.  For example, on Windows, it's not possible to
 *   express a path on the "D:" drive as relative to a base path on the "C:"
 *   drive, since each drive letter has an independent root folder; there's
 *   no namespace entity enclosing a drive letter's root folder.  On
 *   Unix-like systems where the entire namespace has a single hierarchical
 *   root, it should always be possible to express any path relative to any
 *   other.
 *   
 *   The result should be a relative path that can be combined with
 *   'basepath' using os_build_full_path() to reconstruct a path that
 *   identifies the same file as the original 'filename' (it's not important
 *   that this procedure would result in the identical string - it just has
 *   to point to the same file).  If it's not possible to express the
 *   filename relative to the base path, fill in 'result_buf' with the
 *   original filename and return false.
 *   
 *   Windows examples:
 *   
 *.    c:\mjr\games | c:\mjr\games\deep.gam  -> deep.gam
 *.    c:\mjr\games | c:\mjr\games\tads\deep.gam  -> tads\deep.gam
 *.    c:\mjr\games | c:\mjr\tads\deep.gam  -> ..\tads\deep.gam
 *.    c:\mjr\games | d:\deep.gam  ->  d:\deep.gam (and return false)
 *   
 *   Mac OS examples:
 *   
 *.    Mac HD:mjr:games | Mac HD:mjr:games:deep.gam -> deep.gam
 *.    Mac HD:mjr:games | Mac HD:mjr:games:tads:deep.gam -> :tads:deep.gam
 *.    Mac HD:mjr:games | Ext Disk:deep.gam -> Ext Disk:deep.gam (return false)
 *   
 *   VMS examples:
 *   
 *.    SYS$:[mjr.games] | SYS$:[mjr.games]deep.gam -> deep.gam
 *.    SYS$:[mjr.games] | SYS$:[mjr.games.tads]deep.gam -> [.tads]deep.gam
 *.    SYS$:[mjr.games] | SYS$:[mjr.tads]deep.gam -> [-.tads]deep.gam
 *.    SYS$:[mjr.games] | DISK$:[mjr]deep.gam -> DISK$[mjr]deep.gam (ret false)
 */
int os_get_rel_path(char *result_buf, size_t result_buf_size,
                    const char *basepath, const char *filename);

/*
 *   Determine if the given file is in the given directory.  Returns true if
 *   so, false if not.  'filename' is a relative or absolute file name;
 *   'path' is a relative or absolute directory path, such as one returned
 *   from os_get_path_name().
 *   
 *   If 'include_subdirs' is true, the function returns true if the file is
 *   either directly in the directory 'path', OR it's in any subdirectory of
 *   'path'.  If 'include_subdirs' is false, the function returns true only
 *   if the file is directly in the given directory.
 *   
 *   If 'match_self' is true, the function returns true if 'filename' and
 *   'path' are the same directory; otherwise it returns false in this case.
 *   
 *   This routine is allowed to return "false negatives" - that is, it can
 *   claim that the file isn't in the given directory even when it actually
 *   is.  The reason is that it's not always possible to determine for sure
 *   that there's not some way for a given file path to end up in the given
 *   directory.  In contrast, a positive return must be reliable.
 *   
 *   If possible, this routine should fully resolve the names through the
 *   file system to determine the path relationship, rather than merely
 *   analyzing the text superficially.  This can be important because many
 *   systems have multiple ways to reach a given file, such as via symbolic
 *   links on Unix; analyzing the syntax alone wouldn't reveal these multiple
 *   pathways.
 *   
 *   SECURITY NOTE: If possible, implementations should fully resolve all
 *   symbolic links, relative paths (e.g., Unix ".."), etc, before rendering
 *   judgment.  One important application for this routine is to determine if
 *   a file is in a sandbox directory, to enforce security restrictions that
 *   prevent a program from accessing files outside of a designated folder.
 *   If the implementation fails to resolve symbolic links or relative paths,
 *   a malicious program or user could bypass the security restriction by,
 *   for example, creating a symbolic link within the sandbox directory that
 *   points to the root folder.  Implementations can avoid this loophole by
 *   converting the file and directory names to absolute paths and resolving
 *   all symbolic links and relative notation before comparing the paths.
 */
int os_is_file_in_dir(const char *filename, const char *path,
                      int include_subdirs, int match_self);


/* ------------------------------------------------------------------------ */
/*
 *   Convert an OS filename path to URL-style format.  This isn't a true URL
 *   conversion; rather, it simply expresses a filename in Unix-style
 *   notation, as a series of path elements separated by '/' characters.
 *   Unlike true URLs, we don't use % encoding or a scheme prefix (file://,
 *   etc).
 *   
 *   The result path never ends in a trailing '/', unless the entire result
 *   path is "/".  This is for consistency; even if the source path ends with
 *   a local path separator, the result doesn't.
 *   
 *   If the local file system syntax uses '/' characters as ordinary filename
 *   characters, these must be replaced with some other suitable character in
 *   the result, since otherwise they'd be taken as path separators when the
 *   URL is parsed.  If possible, the substitution should be reversible with
 *   respect to os_cvt_dir_url(), so that the same URL read back in on this
 *   same platform will produce the same original filename.  One particular
 *   suggestion is that if the local system uses '/' to delimit what would be
 *   a filename extension on other platforms, replace '/' with '.', since
 *   this will provide reversibility as well as a good mapping if the URL is
 *   read back in on another platform.
 *   
 *   The local equivalents of "." and "..", if they exist, are converted to
 *   "." and ".." in the URL notation.
 *   
 *   Examples:
 *   
 *.   Windows: images\rooms\startroom.jpg -> images/rooms/startroom.jpg
 *.   Windows: ..\startroom.jpg -> ../startroom.jpg
 *.   Mac:     :images:rooms:startroom.jpg -> images/rooms/startroom.jpg
 *.   Mac:     ::startroom.jpg -> ../startroom.jpg
 *.   VMS:     [.images.rooms]startroom.jpg -> images/rooms/startroom.jpg
 *.   VMS:     [-.images]startroom.jpg -> ../images/startroom.jpg
 *.   Unix:    images/rooms/startroom.jpg -> images/rooms/startroom.jpg
 *.   Unix:    ../images/startroom.jpg -> ../images/startroom.jpg
 *   
 *   If the local name is an absolute path in the local file system (e.g.,
 *   Unix /file, Windows C:\file), translate as follows.  If the local
 *   operating system uses a volume or device designator (Windows C:, VMS
 *   SYS$DISK:, etc), make the first element of the path the exact local
 *   syntax for the device designator: /C:/ on Windows, /SYS$DISK:/ on VMS,
 *   etc.  Include the local syntax for the device prefix.  For a system like
 *   Unix with a unified file system root ("/"), simply start with the root
 *   directory.  Examples:
 *   
 *.    Windows:  C:\games\deep.gam         -> /C:/games/deep.gam
 *.    Windows:  C:games\deep.gam          -> /C:./games/deep.gam
 *.    Windows:  \\SERVER\DISK\games\deep.gam -> /\\SERVER/DISK/games/deep.gam
 *.    Mac OS 9: Hard Disk:games:deep.gam  -> /Hard Disk:/games/deep.gam
 *.    VMS:      SYS$DISK:[games]deep.gam  -> /SYS$DISK:/games/deep.gam
 *.    Unix:     /games/deep.gam           -> /games/deep.gam
 *   
 *   Rationale: it's effectively impossible to create a truly portable
 *   representation of an absolute path.  Operating systems are too different
 *   in the way they represent root paths, and even if that were solvable, a
 *   root path is essentially unusable across machines anyway because it
 *   creates a dependency on the contents of a particular machine's disk.  So
 *   if we're called upon to translate an absolute path, we can forget about
 *   trying to be truly portable and instead focus on round-trip fidelity -
 *   i.e., making sure that applying os_cvt_url_dir() to our result recovers
 *   the exact original path, assuming it's done on the same operating
 *   system.  The approach outlined above should achieve round-trip fidelity
 *   when a local path is converted to a URL and back on the same machine,
 *   since the local URL-to-path converter should recognize its own special
 *   type of local absolute path prefix.  It also produces reasonable results
 *   on other platforms - see the os_cvt_url_dir() comments below for
 *   examples of the decoding results for absolute paths moved to new
 *   platforms.  The result when a device-rooted absolute path is encoded on
 *   one machine and then decoded on another will generally be a local path
 *   with a root on the default device/volume and an outermost directory with
 *   a name based on the original machine's device/volume name.  This
 *   obviously won't reproduce the exact original path, but since that's
 *   impossible anyway, this is probably as good an approximation as we can
 *   create.
 *   
 *   Character sets: the input could be in local or UTF-8 character sets.
 *   The implementation shouldn't care, though - just treat bytes in the
 *   range 0-127 as plain ASCII, and everything else as opaque.  I.e., do not
 *   quote or otherwise modify characters outside the 0-127 range.  
 */
void os_cvt_dir_url(char *result_buf, size_t result_buf_size,
                    const char *src_path);

/*
 *   Convert a URL-style path into a filename path expressed in the local
 *   file system's syntax.  Fills in result_buf with a file path, constructed
 *   using the local file system syntax, that corresponds to the path in
 *   src_url expressed in URL-style syntax.  Examples:
 *   
 *   images/rooms/startroom.jpg -> 
 *.   Windows   -> images\rooms\startroom.jpg
 *.   Mac OS 9  -> :images:rooms:startroom.jpg
 *.   VMS       -> [.images.rooms]startroom.jpg
 *   
 *   The source format isn't a true URL; it's simply a series of path
 *   elements separated by '/' characters.  Unlike true URLs, our input
 *   format doesn't use % encoding and doesn't have a scheme (file://, etc).
 *   (Any % in the source is treated as an ordinary character and left as-is,
 *   even if it looks like a %XX sequence.  Anything that looks like a scheme
 *   prefix is left as-is, with any // treated as path separators.
 *   
 *   images/file%20name.jpg ->
 *.   Windows   -> images\file%20name.jpg
 *   
 *   file://images/file.jpg ->
 *.   Windows   -> file_\\images\file.jpg
 *   
 *   Any characters in the path that are invalid in the local file system
 *   naming rules are converted to "_", unless "_" is itself invalid, in
 *   which case they're converted to "X".  One exception is that if '/' is a
 *   valid local filename character (rather than a path separator as it is on
 *   Unix and Windows), it can be used as the replacement for the character
 *   that os_cvt_dir_url uses as its replacement for '/', so that this
 *   substitution is reversible when a URL is generated and then read back in
 *   on this same platform.
 *   
 *   images/file:name.jpg ->
 *.   Windows   -> images\file_name.jpg
 *.   Mac OS 9  -> :images:file_name.jpg
 *.   Unix      -> images/file:name.jpg
 *   
 *   The path elements "." and ".." are specifically defined as having their
 *   Unix meanings: "." is an alias for the preceding path element, or the
 *   working directory if it's the first element, and ".." is an alias for
 *   the parent of the preceding element.  When these appear as path
 *   elements, this routine translates them to the appropriate local
 *   conventions.  "." may be translated simply by removing it from the path,
 *   since it reiterates the previous path element.  ".." may be translated
 *   by removing the previous element - HOWEVER, if ".." appears as the first
 *   element, it has to be retained and translated to the equivalent local
 *   notation, since it will have to be applied later, when the result_buf
 *   path is actually used to open a file, at which point it will combined
 *   with the working directory or another base path.
 *   
 *.  /images/../file.jpg -> [Windows] file.jpg
 *.  ../images/file.jpg ->
 *.   Windows  -> ..\images\file.jpg
 *.   Mac OS 9 -> ::images:file.jpg
 *.   VMS      -> [-.images]file.jpg
 *   
 *   If the URL path is absolute (starts with a '/'), the routine inspects
 *   the path to see if it was created by the same OS, according to the local
 *   rules for converting absolute paths in os_cvt_dir_url() (see).  If so,
 *   we reverse the encoding done there.  If it doesn't appear that the name
 *   was created by the same operating system - that is, if reversing the
 *   encoding doesn't produce a valid local filename - then we create a local
 *   absolute path as follows.  If the local system uses device/volume
 *   designators, we start with the current working device/volume or some
 *   other suitable default volume.  We then add the first element of the
 *   path, if any, as the root directory name, applying the usual "_" or "X"
 *   substitution for any characters that aren't allowed in local names.  The
 *   rest of the path is handled in the usual fashion.
 *   
 *.  /images/file.jpg ->
 *.    Windows -> \images\file.jpg
 *.    Unix    -> /images/file.jpg
 *   
 *.  /c:/images/file.jpg ->
 *.    Windows -> c:\images\file.jpg
 *.    Unix    -> /c:/images/file.jpg
 *.    VMS     -> SYS$DISK:[c__.images]file.jpg
 *   
 *.  /Hard Disk:/images/file.jpg ->
 *.    Windows -> \Hard Disk_\images\file.jpg
 *.    Unix    -> SYS$DISK:[Hard_Disk_.images]file.jpg
 *   
 *   Note how the device/volume prefix becomes the top-level directory when
 *   moving a path across machines.  It's simply not possible to reconstruct
 *   the exact original path in such cases, since device/volume syntax rules
 *   have little in common across systems.  But this seems like a good
 *   approximation in that (a) it produces a valid local path, and (b) it
 *   gives the user a reasonable basis for creating a set of folders to mimic
 *   the original source system, if they want to use that approach to port
 *   the data rather than just changing the paths internally in the source
 *   material.
 *   
 *   Character sets: use the same rules as for os_cvt_dir_url().  
 */
void os_cvt_url_dir(char *result_buf, size_t result_buf_size,
                    const char *src_url);


/* ------------------------------------------------------------------------ */
/*
 *   Get a suitable seed for a random number generator; should use the system
 *   clock or some other source of an unpredictable and changing seed value.
 */
void os_rand(long *val);

/*
 *   Generate random bytes for use in seeding a PRNG (pseudo-random number
 *   generator).  This is an extended version of os_rand() for PRNGs that use
 *   large seed vectors containing many bytes, rather than the simple 32-bit
 *   seed that os_rand() assumes.
 *   
 *   As with os_rand(), this function isn't meant to be used directly as a
 *   random number source for ongoing use - instead, this is intended mostly
 *   for seeding a PRNG, which will then be used as the primary source of
 *   random numbers.  The big problem with using this routine directly as a
 *   randomness source is that some implementations might rely heavily on
 *   environmental randomness, such as the real-time clock or OS usage
 *   statistics.  Such sources tend to provide reasonable entropy from one
 *   run to the next, but not within a single session, as the underlying data
 *   sources don't change rapidly enough.
 *   
 *   Ideally, this routine should generate *truly* random bytes obtained from
 *   hardware sources.  Not all systems can provide that, though, so true
 *   randomness isn't guaranteed.  Here are the suggested implementation
 *   options, in descending order of desirability:
 *   
 *   1.  Use a hardware source of true randomness, such as a /dev/rand type
 *   of device.  However, note that this call should return reasonably
 *   quickly, so always use a non-blocking source.  Some Unix /dev/rand
 *   devices, for example, can block indefinitely to allow sufficient entropy
 *   to accumulate.
 *   
 *   2. Use a cryptographic random number source provided by the OS.  Some
 *   systems provide this as an API service.  If going this route, be sure
 *   that the OS generator is itself "seeded" with some kind of true
 *   randomness source, as it defeats the whole purpose if you always return
 *   a fixed pseudo-random sequence each time the program runs.
 *   
 *   3. Use whatever true random sources are available locally to seed a
 *   software pseudo-random number generator, then generate bytes from your
 *   PRNG.  Some commonly available sources of true randomness are a
 *   high-resolution timer, the system clock, the current process ID,
 *   logged-in user ID, environment variables, uninitialized pages of memory,
 *   the IP address; each of these sources might give you a few bits of
 *   entropy at best, so the best bet is to use an ensemble.  You could, for
 *   example, concatenate a bunch of this type of information together and
 *   calculate an MD5 or SHA1 hash to mix the bits more thoroughly.  For the
 *   PRNG, use a cryptographic generator.  (You could use the ISAAC generator
 *   from TADS 3, as that's a crypto PRNG, but it's probably better to use a
 *   different generator here since TADS 3 is going to turn around and use
 *   this function's output to seed ISAAC - seeding one ISAAC instance with
 *   another ISAAC's output seems likely to magnify any weaknesses in the
 *   ISAAC algorithm.)  Note that this option is basically the DIY version of
 *   option 2.  Option 2 is better because the OS probably has access to
 *   better sources of true randomness than an application does.  
 */
void os_gen_rand_bytes(unsigned char *buf, size_t len);


/* ------------------------------------------------------------------------ */
/*
 *   Display routines.
 *   
 *   Our display model is a simple stdio-style character stream.
 *   
 *   In addition, we provide an optional "status line," which is a
 *   non-scrolling area where a line of text can be displayed.  If the status
 *   line is supported, text should only be displayed in this area when
 *   os_status() is used to enter status-line mode (mode 1); while in status
 *   line mode, text is written to the status line area, otherwise (mode 0)
 *   it's written to the normal main text area.  The status line is normally
 *   shown in a different color to set it off from the rest of the text.
 *   
 *   The OS layer can provide its own formatting (word wrapping in
 *   particular) if it wants, in which case it should also provide pagination
 *   using os_more_prompt().  
 */

/*
 *   OS_MAXWIDTH - the maximum width of a line of text.  Most platforms use
 *   135 for this, but you can use more or less as appropriate.  If you use
 *   OS-level line wrapping, then the true width of a text line is
 *   irrelevant, and the portable code will use this merely for setting its
 *   internal buffer sizes.
 *   
 *   This must be defined in the os_xxx.h header file for each platform.
 */
/*#define OS_MAXWIDTH 135 - example only: define for real in os_xxx.h header*/

/*
 *   Print a string on the console.  These routines come in two varieties:
 *   
 *   os_printz - write a NULL-TERMINATED string
 *.  os_print - write a COUNTED-LENGTH string, which may not end with a null
 *   
 *   These two routines are identical except that os_printz() takes a string
 *   which is terminated by a null byte, and os_print() instead takes an
 *   explicit length, and a string that may not end with a null byte.
 *   
 *   os_printz(str) may be implemented as simply os_print(str, strlen(str)).
 *   
 *   The string is written in one of three ways, depending on the status mode
 *   set by os_status():
 *   
 *   status mode == 0 -> write to main text window
 *.  status mode == 1 -> write to status line
 *.  anything else -> do not display the text at all
 *   
 *   Implementations are free to omit any status line support, in which case
 *   they should simply suppress all output when the status mode is anything
 *   other than zero.
 *   
 *   The following special characters must be recognized in the displayed
 *   text:
 *   
 *   '\n' - newline: end the current line and move the cursor to the start of
 *   the next line.  If the status line is supported, and the current status
 *   mode is 1 (i.e., displaying in the status line), then two special rules
 *   apply to newline handling: newlines preceding any other text should be
 *   ignored, and a newline following any other text should set the status
 *   mode to 2, so that all subsequent output is suppressed until the status
 *   mode is changed with an explicit call by the client program to
 *   os_status().
 *   
 *   '\r' - carriage return: end the current line and move the cursor back to
 *   the beginning of the current line.  Subsequent output is expected to
 *   overwrite the text previously on this same line.  The implementation
 *   may, if desired, IMMEDIATELY clear the previous text when the '\r' is
 *   written, rather than waiting for subsequent text to be displayed.
 *   
 *   All other characters may be assumed to be ordinary printing characters.
 *   The routine need not check for any other special characters.
 *   
 */
void os_printz(const char *str);
void os_print(const char *str, size_t len);

/*
 *   Print to the debugger console.  These routines are for interactive
 *   debugger builds only: they display the given text to a separate window
 *   within the debugger UI (separate from the main game command window)
 *   where the debugger displays status information specific to the debugging
 *   session (such as compiler/build output, breakpoint status messages,
 *   etc).  For example, TADS Workbench on Windows displays these messages in
 *   its "Debug Log" window.
 *   
 *   These routines only need to be implemented for interactive debugger
 *   builds, such as TADS Workbench on Windows.  These can be omitted for
 *   regular interpreter builds.  
 */
void os_dbg_printf(const char *fmt, ...);
void os_dbg_vprintf(const char *fmt, va_list args);

/*
 *   Allocating sprintf and vsprintf.  These work like the regular C library
 *   sprintf and vsprintf funtions, but they allocate a return buffer that's
 *   big enough to hold the result, rather than formatting into a caller's
 *   buffer.  This protects against buffer overruns and ensures that the
 *   result isn't truncated.
 *   
 *   On return, '*bufptr' is filled in with a pointer to a buffer allocated
 *   with osmalloc().  This buffer contains the formatted string result.  The
 *   caller is responsible for freeing the buffer by calling osfree().
 *   *bufptr can be null on return if an error occurs.
 *   
 *   The return value is the number of bytes written to the allocated buffer,
 *   not including the null terminator.  If an error occurs, the return value
 *   is -1 and *bufptr is undefined.
 *   
 *   Many modern C libraries provide equivalents of these, usually called
 *   asprintf() and vasprintf(), respectively.  
 */
/* int os_asprintf(char **bufptr, const char *fmt, ...); */
int os_vasprintf(char **bufptr, const char *fmt, va_list ap);


/* 
 *   Set the status line mode.  There are three possible settings:
 *   
 *   0 -> main text mode.  In this mode, all subsequent text written with
 *   os_print() and os_printz() is to be displayed to the main text area.
 *   This is the normal mode that should be in effect initially.  This mode
 *   stays in effect until an explicit call to os_status().
 *   
 *   1 -> statusline mode.  In this mode, text written with os_print() and
 *   os_printz() is written to the status line, which is usually rendered as
 *   a one-line area across the top of the terminal screen or application
 *   window.  In statusline mode, leading newlines ('\n' characters) are to
 *   be ignored, and any newline following any other character must change
 *   the mode to 2, as though os_status(2) had been called.
 *   
 *   2 -> suppress mode.  In this mode, all text written with os_print() and
 *   os_printz() must simply be ignored, and not displayed at all.  This mode
 *   stays in effect until an explicit call to os_status().  
 */
void os_status(int stat);

/* get the status line mode */
int os_get_status();

/* 
 *   Set the score value.  This displays the given score and turn counts on
 *   the status line.  In most cases, these values are displayed at the right
 *   edge of the status line, in the format "score/turns", but the format is
 *   up to the implementation to determine.  In most cases, this can simply
 *   be implemented as follows:
 *   
 *.  void os_score(int score, int turncount)
 *.  {
 *.     char buf[40];
 *.     sprintf(buf, "%d/%d", score, turncount);
 *.     os_strsc(buf);
 *.  }
 */
void os_score(int score, int turncount);

/* display a string in the score area in the status line */
void os_strsc(const char *p);

/* clear the screen */
void oscls(void);

/* redraw the screen */
void os_redraw(void);

/* flush any buffered display output */
void os_flush(void);

/*
 *   Update the display - process any pending drawing immediately.  This
 *   only needs to be implemented for operating systems that use
 *   event-driven drawing based on window invalidations; the Windows and
 *   Macintosh GUI's both use this method for drawing window contents.
 *   
 *   The purpose of this routine is to refresh the display prior to a
 *   potentially long-running computation, to avoid the appearance that the
 *   application is frozen during the computation delay.
 *   
 *   Platforms that don't need to process events in the main thread in order
 *   to draw their window contents do not need to do anything here.  In
 *   particular, text-mode implementations generally don't need to implement
 *   this routine.
 *   
 *   This routine doesn't absolutely need a non-empty implementation on any
 *   platform, but it will provide better visual feedback if implemented for
 *   those platforms that do use event-driven drawing.  
 */
void os_update_display();


/* ------------------------------------------------------------------------ */
/*
 *   Set text attributes.  Text subsequently displayed through os_print() and
 *   os_printz() are to be displayed with the given attributes.
 *   
 *   'attr' is a (bitwise-OR'd) combination of OS_ATTR_xxx values.  A value
 *   of zero indicates normal text, with no extra attributes.  
 */
void os_set_text_attr(int attr);

/* attribute code: bold-face */
#define OS_ATTR_BOLD     0x0001

/* attribute code: italic */
#define OS_ATTR_ITALIC   0x0002

/*
 *   Abstract attribute codes.  Each platform can choose a custom rendering
 *   for these by #defining them before this point, in the OS-specific header
 *   (osdos.h, osmac.h, etc).  We provide *default* definitions in case the
 *   platform doesn't define these.
 *   
 *   For compatibility with past versions, we treat HILITE, EM, and BOLD as
 *   equivalent.  Platforms that can display multiple kinds of text
 *   attributes (boldface and italic, say) should feel free to use more
 *   conventional HTML mappings, such as EM->italic and STRONG->bold.  
 */

/* 
 *   "Highlighted" text, as appropriate to the local platform.  On most
 *   text-mode platforms, the only kind of rendering variation possible is a
 *   brighter or intensified color.  If actual bold-face is available, that
 *   can be used instead.  This is the attribute used for text enclosed in a
 *   TADS2 "\( \)" sequence.  
 */
#ifndef OS_ATTR_HILITE
# define OS_ATTR_HILITE  OS_ATTR_BOLD
#endif

/* HTML <em> attribute - by default, map this to bold-face */
#ifndef OS_ATTR_EM
# define OS_ATTR_EM      OS_ATTR_BOLD
#endif

/* HTML <strong> attribute - by default, this has no effect */
#ifndef OS_ATTR_STRONG
# define OS_ATTR_STRONG  0
#endif


/* ------------------------------------------------------------------------ */
/*
 *   Colors.
 *   
 *   There are two ways of encoding a color.  First, a specific color can be
 *   specified as an RGB (red-green-blue) value, with discreet levels for
 *   each component's intensity, ranging from 0 to 255.  Second, a color can
 *   be "parameterized," which doesn't specify a color in absolute terms but
 *   rather specified one of a number of pre-defined *types* of colors;
 *   these pre-defined types can be chosen by the OS implementation, or, on
 *   some systems, selected by the user via a preferences mechanism.
 *   
 *   The os_color_t type encodes a color in 32 bits.  The high-order 8 bits
 *   of a color value give the parameterized color identifier, or are set to
 *   zero to indicate an RGB color.  An RGB color is encoded in the
 *   low-order 24 bits, via the following formula:
 *   
 *   (R << 16) + (G << 8) + B
 *   
 *   R specifies the intensity of the red component of the color, G green,
 *   and B blue.  Each of R, G, and B must be in the range 0-255.  
 */
typedef unsigned long os_color_t;

/* encode an R, G, B triplet into an os_color_t value */
#define os_rgb_color(r, g, b) (((r) << 16) + ((g) << 8) + (b))

/* 
 *   Determine if a color is given as an RGB value or as a parameterized
 *   color value.  Returns true if the color is given as a parameterized
 *   color (one of the OS_COLOR_xxx values), false if it's given as an
 *   absolute RGB value.  
 */
#define os_color_is_param(color) (((color) & 0xFF000000) != 0)

/* get the red/green/blue components of an os_color_t value */
#define os_color_get_r(color) ((int)(((color) >> 16) & 0xFF))
#define os_color_get_g(color) ((int)(((color) >> 8) & 0xFF))
#define os_color_get_b(color) ((int)((color) & 0xFF))

/*
 *   Parameterized color codes.  These are os_color_t values that indicate
 *   colors by type, rather than by absolute RGB values.  
 */

/* 
 *   "transparent" - applicable to backgrounds only, this specifies that the
 *   current screen background color should be used 
 */
#define OS_COLOR_P_TRANSPARENT ((os_color_t)0x01000000)

/* "normal text" color (as set via user preferences, if applicable) */
#define OS_COLOR_P_TEXT        ((os_color_t)0x02000000)

/* normal text background color (from user preferences) */
#define OS_COLOR_P_TEXTBG      ((os_color_t)0x03000000)

/* "status line" text color (as set via user preferences, if applicable) */
#define OS_COLOR_P_STATUSLINE  ((os_color_t)0x04000000)

/* status line background color (from user preferences) */
#define OS_COLOR_P_STATUSBG    ((os_color_t)0x05000000)

/* input text color (as set via user preferences, if applicable) */
#define OS_COLOR_P_INPUT       ((os_color_t)0x06000000)

/*
 *   Set the text foreground and background colors.  This sets the text
 *   color for subsequent os_printf() and os_vprintf() calls.
 *   
 *   The background color can be OS_COLOR_TRANSPARENT, in which case the
 *   background color is "inherited" from the current screen background.
 *   Note that if the platform is capable of keeping old text for
 *   "scrollback," then the transparency should be a permanent attribute of
 *   the character - in other words, it should not be mapped to the current
 *   screen color in the scrollback buffer, because doing so would keep the
 *   current screen color even if the screen color changes in the future. 
 *   
 *   Text color support is optional.  If the platform doesn't support text
 *   colors, this can simply do nothing.  If the platform supports text
 *   colors, but the requested color or attributes cannot be displayed, the
 *   implementation should use the best available approximation.  
 */
void os_set_text_color(os_color_t fg, os_color_t bg);

/*
 *   Set the screen background color.  This sets the text color for the
 *   background of the screen.  If possible, this should immediately redraw
 *   the main text area with this background color.  The color is given as an
 *   OS_COLOR_xxx value.
 *   
 *   If the platform is capable of redisplaying the existing text, then any
 *   existing text that was originally displayed with 'transparent'
 *   background color should be redisplayed with the new screen background
 *   color.  In other words, the 'transparent' background color of previously
 *   drawn text should be a permanent attribute of the character - the color
 *   should not be mapped on display to the then-current background color,
 *   because doing so would lose the transparency and thus retain the old
 *   screen color on a screen color change.  
 */
void os_set_screen_color(os_color_t color);


/* ------------------------------------------------------------------------ */
/* 
 *   os_plain() - Use plain ascii mode for the display.  If possible and
 *   necessary, turn off any text formatting effects, such as cursor
 *   positioning, highlighting, or coloring.  If this routine is called,
 *   the terminal should be treated as a simple text stream; users might
 *   wish to use this mode for things like text-to-speech converters.
 *   
 *   Purely graphical implementations that cannot offer a textual mode
 *   (such as Mac OS or Windows) can ignore this setting.
 *   
 *   If this routine is to be called, it must be called BEFORE os_init().
 *   The implementation should set a flag so that os_init() will know to
 *   set up the terminal for plain text output.  
 */
#ifndef os_plain
/* 
 *   some platforms (e.g. Mac OS) define this to be a null macro, so don't
 *   define a prototype in those cases 
 */
void os_plain(void);
#endif

/*
 *   Set the game title.  The output layer calls this routine when a game
 *   sets its title (via an HTML <title> tag, for example).  If it's
 *   convenient to do so, the OS layer can use this string to set a window
 *   caption, or whatever else makes sense on each system.  Most
 *   character-mode implementations will provide an empty implementation,
 *   since there's not usually any standard way to show the current
 *   application title on a character-mode display.  
 */
void os_set_title(const char *title);

/*
 *   Show the system-specific MORE prompt, and wait for the user to respond.
 *   Before returning, remove the MORE prompt from the screen.
 *   
 *   This routine is only used and only needs to be implemented when the OS
 *   layer takes responsibility for pagination; this will be the case on
 *   most systems that use proportionally-spaced (variable-pitch) fonts or
 *   variable-sized windows, since on such platforms the OS layer must do
 *   most of the formatting work, leaving the standard output layer unable
 *   to guess where pagination should occur.
 *   
 *   If the portable output formatter handles the MORE prompt, which is the
 *   usual case for character-mode or terminal-style implementations, this
 *   routine is not used and you don't need to provide an implementation.
 *   Note that HTML TADS provides an implementation of this routine, because
 *   the HTML renderer handles line breaking and thus must handle
 *   pagination.  
 */
void os_more_prompt();

/*
 *   Interpreter Class Configuration.
 *   
 *   If this is a TEXT-ONLY interpreter: DO NOT define USE_HTML.
 *   
 *   If this is a MULTIMEDIA (HTML TADS) intepreter: #define USE_HTML
 *   
 *   (This really should be called something like OS_USE_HTML - the USE_ name
 *   is for historical reasons.  This purpose of this macro is to configure
 *   the tads 2 VM-level output formatter's line breaking and MORE mode
 *   behavior.  In HTML mode, the VM-level formatter knows that it's feeding
 *   its output to a page layout engine, so the VM-level output is just a
 *   text stream.  In plain-text mode, the VM formatter *is* the page layout
 *   engine, so it needs to do all of the word wrapping and MORE prompting
 *   itself.  The tads 3 output layer does NOT use this macro for its
 *   equivalent configuration, but instead has different .cpp files for the
 *   different modes, and you simply link in the one for the configuration
 *   you want.)  
 */
/* #define USE_HTML */


/*
 *   Enter HTML mode.  This is only used when the run-time is compiled
 *   with the USE_HTML flag defined.  This call instructs the renderer
 *   that HTML sequences should be parsed; until this call is made, the
 *   renderer should not interpret output as HTML.  Non-HTML
 *   implementations do not need to define this routine, since the
 *   run-time will not call it if USE_HTML is not defined.  
 */
void os_start_html(void);

/* exit HTML mode */
void os_end_html(void);

/*
 *   Global variables with the height and width (in character cells - rows
 *   and columns) of the main text display area into which os_printf
 *   displays.  The height and width are given in text lines and character
 *   columns, respectively.  The portable code can use these values to
 *   format text for display via os_printf(); for example, the caller can
 *   use the width to determine where to put line breaks.
 *   
 *   These values are only needed for systems where os_printf() doesn't
 *   perform its own word-wrap formatting.  On systems such as the Mac,
 *   where os_printf() performs word wrapping, these sizes aren't really
 *   important because the portable code doesn't need to perform any real
 *   formatting.
 *   
 *   These variables reflect the size of the "main text area," which is the
 *   area of the screen excluding the status line and any "banner" windows
 *   (as created with the os_banner_xxx() interfaces).
 *   
 *   The OS code must initialize these variables during start-up, and must
 *   adjust them whenever the display size is changed by user action or
 *   other external events (for example, if we're running inside a terminal
 *   window, and the user resizes the window, the OS code must recalculate
 *   the layout and adjust these accordingly).  
 */
extern int G_os_pagelength;
extern int G_os_linewidth;

/*
 *   Global flag that tells the output formatter whether to count lines
 *   that it's displaying against the total on the screen so far.  If this
 *   variable is true, lines are counted, and the screen is paused with a
 *   [More] message when it's full.  When not in MORE mode, lines aren't
 *   counted.  This variable should be set to false when displaying text
 *   that doesn't count against the current page, such as status line
 *   information.
 *   
 *   This flag should not be modified by OS code.  Instead, the output
 *   formatter will set this flag according to its current state; the OS
 *   code can use this flag to determine whether or not to display a MORE
 *   prompt during os_printf()-type operations.  Note that this flag is
 *   normally interesting to the OS code only when the OS code itself is
 *   handling the MORE prompt.  
 */
extern int G_os_moremode;

/*
 *   Global buffer containing the name of the byte-code file (the "game
 *   file") loaded into the VM.  This is used only where applicable, which
 *   generally means in TADS Interpreter builds.  In other application
 *   builds, this is simply left empty.  The application is responsible for
 *   setting this during start-up (or wherever else the byte-code filename
 *   becomes known or changes).  
 */
extern char G_os_gamename[OSFNMAX];

/*
 *   Set non-stop mode.  This tells the OS layer that it should disable any
 *   MORE prompting it would normally do.
 *   
 *   This routine is needed only when the OS layer handles MORE prompting; on
 *   character-mode platforms, where the prompting is handled in the portable
 *   console layer, this can be a dummy implementation.  
 */
void os_nonstop_mode(int flag);

/* 
 *   Update progress display with current info, if appropriate.  This can
 *   be used to provide a status display during compilation.  Most
 *   command-line implementations will just ignore this notification; this
 *   can be used for GUI compiler implementations to provide regular
 *   display updates during compilation to show the progress so far.  
 */
/* void os_progress(const char *fname, unsigned long linenum); */

/* 
 *   Set busy cursor.  If 'flag' is true, provide a visual representation
 *   that the system or application is busy doing work.  If 'flag' is
 *   false, remove any visual "busy" indication and show normal status.
 *   
 *   We provide a prototype here if your osxxx.h header file does not
 *   #define a macro for os_csr_busy.  On many systems, this function has
 *   no effect at all, so the osxxx.h header file simply #define's it to
 *   do an empty macro.  
 */
#ifndef os_csr_busy
void os_csr_busy(int flag);
#endif


/* ------------------------------------------------------------------------ */
/*
 *   User Input Routines
 */

/*
 *   Ask the user for a filename, using a system-dependent dialog or other
 *   mechanism.  Returns one of the OS_AFE_xxx status codes (see below).
 *   
 *   prompt_type is the type of prompt to provide -- this is one of the
 *   OS_AFP_xxx codes (see below).  The OS implementation doesn't need to
 *   pay any attention to this parameter, but it can be used if desired to
 *   determine the type of dialog to present if the system provides
 *   different types of dialogs for different types of operations.
 *   
 *   file_type is one of the OSFTxxx codes for system file type.  The OS
 *   implementation is free to ignore this information, but can use it to
 *   filter the list of files displayed if desired; this can also be used
 *   to apply a default suffix on systems that use suffixes to indicate
 *   file type.  If OSFTUNK is specified, it means that no filtering
 *   should be performed, and no default suffix should be applied.  
 */
int os_askfile(const char *prompt, char *fname_buf, int fname_buf_len,
               int prompt_type, os_filetype_t file_type);

/* 
 *   os_askfile status codes 
 */

/* success */
#define OS_AFE_SUCCESS  0 

/* 
 *   Generic failure - this is largely provided for compatibility with
 *   past versions, in which only zero and non-zero error codes were
 *   meaningful; since TRUE is defined as 1 on most platforms, we assume
 *   that 1 is probably the generic non-zero error code that most OS
 *   implementations have traditionally used.  In addition, this can be
 *   used to indicate any other error for which there is no more specific
 *   error code.  
 */
#define OS_AFE_FAILURE  1

/* user cancelled */
#define OS_AFE_CANCEL   2

/* 
 *   os_askfile prompt types
 *   
 *   Important note: do not change these values when porting TADS.  These
 *   values can be used by games, so they must be the same on all
 *   platforms.  
 */
#define OS_AFP_OPEN    1     /* choose an existing file to open for reading */
#define OS_AFP_SAVE    2          /* choose a filename for saving to a file */


/* 
 *   Read a string of input.  Fills in the buffer with a null-terminated
 *   string containing a line of text read from the standard input.  The
 *   returned string should NOT contain a trailing newline sequence.  On
 *   success, returns 'buf'; on failure, including end of file, returns a
 *   null pointer.  
 */
unsigned char *os_gets(unsigned char *buf, size_t bufl);

/*
 *   Read a string of input with an optional timeout.  This behaves like
 *   os_gets(), in that it allows the user to edit a line of text (ideally
 *   using the same editing keys that os_gets() does), showing the line of
 *   text under construction during editing.  This routine differs from
 *   os_gets() in that it returns if the given timeout interval expires
 *   before the user presses Return (or the local equivalent).
 *   
 *   If the user presses Return before the timeout expires, we store the
 *   command line in the given buffer, just as os_gets() would, and we return
 *   OS_EVT_LINE.  We also update the display in the same manner that
 *   os_gets() would, by moving the cursor to a new line and scrolling the
 *   displayed text as needed.
 *   
 *   If a timeout occurs before the user presses Return, we store the command
 *   line so far in the given buffer, statically store the cursor position,
 *   insert mode, buffer text, and anything else relevant to the editing
 *   state, and we return OS_EVT_TIMEOUT.
 *   
 *   If the implementation does not support the timeout operation, this
 *   routine should simply return OS_EVT_NOTIMEOUT immediately when called;
 *   the routine should not allow the user to perform any editing if the
 *   timeout is not supported.  Callers must use the ordinary os_gets()
 *   routine, which has no timeout capabilities, if the timeout is not
 *   supported.
 *   
 *   When we return OS_EVT_TIMEOUT, the caller is responsible for doing one
 *   of two things.
 *   
 *   The first possibility is that the caller performs some work that doesn't
 *   require any display operations (in other words, the caller doesn't
 *   invoke os_printf, os_getc, or anything else that would update the
 *   display), and then calls os_gets_timeout() again.  In this case, we will
 *   use the editing state that we statically stored before we returned
 *   OS_EVT_TIMEOUT to continue editing where we left off.  This allows the
 *   caller to perform some computation in the middle of user command editing
 *   without interrupting the user - the extra computation is transparent to
 *   the user, because we act as though we were still in the midst of the
 *   original editing.
 *   
 *   The second possibility is that the caller wants to update the display.
 *   In this case, the caller must call os_gets_cancel() BEFORE making any
 *   display changes.  Then, the caller must do any post-input work of its
 *   own, such as updating the display mode (for example, closing HTML font
 *   tags that were opened at the start of the input).  The caller is now
 *   free to do any display work it wants.
 *   
 *   If we have information stored from a previous call that was interrupted
 *   by a timeout, and os_gets_cancel(TRUE) was never called, we will resume
 *   editing where we left off when the cancelled call returned; this means
 *   that we'll restore the cursor position, insertion state, and anything
 *   else relevant.  Note that if os_gets_cancel(FALSE) was called, we must
 *   re-display the command line under construction, but if os_gets_cancel()
 *   was never called, we will not have to make any changes to the display at
 *   all.
 *   
 *   Note that when resuming an interrupted editing session (interrupted via
 *   os_gets_cancel()), the caller must re-display the prompt prior to
 *   invoking this routine.
 *   
 *   Note that we can return OS_EVT_EOF in addition to the other codes
 *   mentioned above.  OS_EVT_EOF indicates that an error occurred reading,
 *   which usually indicates that the application is being terminated or that
 *   some hardware error occurred reading the keyboard.  
 *   
 *   If 'use_timeout' is false, the timeout should be ignored.  Without a
 *   timeout, the function behaves the same as os_gets(), except that it will
 *   resume editing of a previously-interrupted command line if appropriate.
 *   (This difference is why the timeout is optional: a caller might not need
 *   a timeout, but might still want to resume a previous input that did time
 *   out, in which case the caller would invoke this routine with
 *   use_timeout==FALSE.  The regular os_gets() would not satisfy this need,
 *   because it cannot resume an interrupted input.)
 *   
 *   Note that a zero timeout has the same meaning as for os_get_event(): if
 *   input is available IMMEDIATELY, return the input, otherwise return
 *   immediately with the OS_EVT_TIMEOUT result code.  
 */
int os_gets_timeout(unsigned char *buf, size_t bufl,
                    unsigned long timeout_in_milliseconds, int use_timeout);

/*
 *   Cancel an interrupted editing session.  This MUST be called if any
 *   output is to be displayed after a call to os_gets_timeout() returns
 *   OS_EVT_TIMEOUT.
 *   
 *   'reset' indicates whether or not we will forget the input state saved
 *   by os_gets_timeout() when it last returned.  If 'reset' is true, we'll
 *   clear the input state, so that the next call to os_gets_timeout() will
 *   start with an empty input buffer.  If 'reset' is false, we will retain
 *   the previous input state, if any; this means that the next call to
 *   os_gets_timeout() will re-display the same input buffer that was under
 *   construction when it last returned.
 *   
 *   This routine need not be called if os_gets_timeout() is to be called
 *   again with no other output operations between the previous
 *   os_gets_timeout() call and the next one.
 *   
 *   Note that this routine needs only a trivial implementation when
 *   os_gets_timeout() is not supported (i.e., the function always returns
 *   OS_EVT_NOTIMEOUT).  
 */
void os_gets_cancel(int reset);

/* 
 *   Read a character from the keyboard.  For extended keystrokes, this
 *   function returns zero, and then returns the CMD_xxx code for the
 *   extended keystroke on the next call.  For example, if the user presses
 *   the up-arrow key, the first call to os_getc() should return 0, and the
 *   next call should return CMD_UP.  Refer to the CMD_xxx codes below.
 *   
 *   os_getc() should return a high-level, translated command code for
 *   command editing.  This means that, where a functional interpretation of
 *   a key and the raw key-cap interpretation both exist as CMD_xxx codes,
 *   the functional interpretation should be returned.  For example, on Unix,
 *   Ctrl-E is conventionally used in command editing to move to the end of
 *   the line, following Emacs key bindings.  Hence, os_getc() should return
 *   CMD_END for this keystroke, rather than a single 0x05 character (ASCII
 *   Ctrl-E), because CMD_END is the high-level command code for the
 *   operation.
 *   
 *   The translation ability of this function allows for system-dependent key
 *   mappings to functional meanings.  
 */
int os_getc(void);

/*
 *   Read a character from the keyboard, following the same protocol as
 *   os_getc() for CMD_xxx codes (i.e., when an extended keystroke is
 *   encountered, os_getc_raw() returns zero, then returns the CMD_xxx code
 *   on the subsequent call).
 *   
 *   This function differs from os_getc() in that this function returns the
 *   low-level, untranslated key code whenever possible.  This means that,
 *   when a functional interpretation of a key and the raw key-cap
 *   interpretation both exist as CMD_xxx codes, this function returns the
 *   key-cap interpretation.  For the Unix Ctrl-E example in the comments
 *   describing os_getc() above, this function should return 5 (the ASCII
 *   code for Ctrl-E), because the ASCII control character interpretation is
 *   the low-level key code.
 *   
 *   This function should return all control keys using their ASCII control
 *   codes, whenever possible.  Similarly, this function should return ASCII
 *   27 for the Escape key, if possible.  
 *   
 *   For keys for which there is no portable ASCII representation, this
 *   should return the CMD_xxx sequence.  So, this function acts exactly the
 *   same as os_getc() for arrow keys, function keys, and other special keys
 *   that have no ASCII representation.  This function returns a
 *   non-translated version ONLY when an ASCII representation exists - in
 *   practice, this means that this function and os_getc() vary only for CTRL
 *   keys and Escape.  
 */
int os_getc_raw(void);


/* wait for a character to become available from the keyboard */
void os_waitc(void);

/*
 *   Constants for os_getc() when returning commands.  When used for
 *   command line editing, special keys (arrows, END, etc.)  should cause
 *   os_getc() to return 0, and return the appropriate CMD_ value on the
 *   NEXT call.  Hence, os_getc() must keep the appropriate information
 *   around statically for the next call when a command key is issued.
 *   
 *   The comments indicate which CMD_xxx codes are "translated" codes and
 *   which are "raw"; the difference is that, when a particular keystroke
 *   could be interpreted as two different CMD_xxx codes, one translated
 *   and the other raw, os_getc() should always return the translated
 *   version of the key, and os_getc_raw() should return the raw version.
 */
#define CMD_UP    1                        /* move up/up arrow (translated) */
#define CMD_DOWN  2                    /* move down/down arrow (translated) */
#define CMD_RIGHT 3                  /* move right/right arrow (translated) */
#define CMD_LEFT  4                    /* move left/left arrow (translated) */
#define CMD_END   5              /* move cursor to end of line (translated) */
#define CMD_HOME  6            /* move cursor to start of line (translated) */
#define CMD_DEOL  7                   /* delete to end of line (translated) */
#define CMD_KILL  8                      /* delete entire line (translated) */
#define CMD_DEL   9                /* delete current character (translated) */
#define CMD_SCR   10                 /* toggle scrollback mode (translated) */
#define CMD_PGUP  11                                /* page up (translated) */
#define CMD_PGDN  12                              /* page down (translated) */
#define CMD_TOP   13                            /* top of file (translated) */
#define CMD_BOT   14                         /* bottom of file (translated) */
#define CMD_F1    15                               /* function key F1 (raw) */
#define CMD_F2    16                               /* function key F2 (raw) */
#define CMD_F3    17                               /* function key F3 (raw) */
#define CMD_F4    18                               /* function key F4 (raw) */
#define CMD_F5    19                               /* function key F5 (raw) */
#define CMD_F6    20                               /* function key F6 (raw) */
#define CMD_F7    21                               /* function key F7 (raw) */
#define CMD_F8    22                               /* function key F8 (raw) */
#define CMD_F9    23                               /* function key F9 (raw) */
#define CMD_F10   24                              /* function key F10 (raw) */
#define CMD_CHOME 25                                  /* control-home (raw) */
#define CMD_TAB   26                                    /* tab (translated) */
#define CMD_SF2   27                                      /* shift-F2 (raw) */
/* not used (obsolete) - 28 */
#define CMD_WORD_LEFT  29      /* word left (ctrl-left on dos) (translated) */
#define CMD_WORD_RIGHT 30    /* word right (ctrl-right on dos) (translated) */
#define CMD_WORDKILL 31                   /* delete word right (translated) */
#define CMD_EOF   32                                   /* end-of-file (raw) */
#define CMD_BREAK 33     /* break (Ctrl-C or local equivalent) (translated) */
#define CMD_INS   34                                    /* insert key (raw) */


/*
 *   ALT-keys - add alphabetical code to CMD_ALT: ALT-A == CMD_ALT + 0,
 *   ALT-B == CMD_ALT + 1, ALT-C == CMD_ALT + 2, etc
 *   
 *   These keys are all raw (untranslated).  
 */
#define CMD_ALT   128                                  /* start of ALT keys */


/* ------------------------------------------------------------------------ */
/*
 *   Event information structure for os_get_event.  The appropriate union
 *   member should be filled in, depending on the type of event that
 *   occurs. 
 */
union os_event_info_t
{
    /* 
     *   OS_EVT_KEY - this returns the one or two characters of the
     *   keystroke.  If the key is an extended key, so that os_getc() would
     *   return a two-character sequence for the keystroke, the first
     *   character should be zero and the second the extended key code.
     *   Otherwise, the first character should simply be the ASCII key code.
     *   
     *   The key code here is the "raw" keycode, equivalent to the codes
     *   returned by os_getc_raw().  Note in particular that this means that
     *   CTRL and Escape keys are presented as one-byte ASCII control
     *   characters, not as two-byte CMD_xxx sequences.  
     *   
     *   For multi-byte character sets (Shift-JIS, for example), note that
     *   os_get_event() must NOT return a complete two-byte character here.
     *   The two bytes here are exclusively used to represent special
     *   CMD_xxx keys (such as arrow keys and function keys).  For a
     *   keystroke that is represented in a multi-byte character set using
     *   more than one byte, os_get_event() must return a series of events.
     *   Return an ordinary OS_EVT_KEY for the first byte of the sequence,
     *   putting the byte in key[0]; then, return the second byte as a
     *   separate OS_EVT_KEY as the next event; and so on for any additional
     *   bytes.  This will allow callers that are not multibyte-aware to
     *   treat multi-byte characters as though they were sequences of
     *   one-byte characters.  
     */
    int key[2];

    /*
     *   OS_EVT_HREF - this returns the text of the HREF as a
     *   null-terminated string.  
     */
    char href[256];

    /* command ID (for OS_EVT_COMMAND) */
    int cmd_id;
};
typedef union os_event_info_t os_event_info_t;

/*
 *   Event types for os_get_event 
 */

/* invalid/no event */
#define OS_EVT_NONE      0x0000

/* OS_EVT_KEY - user typed a key on the keyboard */
#define OS_EVT_KEY       0x0001

/* OS_EVT_TIMEOUT - no event occurred before the timeout elapsed */
#define OS_EVT_TIMEOUT   0x0002

/* 
 *   OS_EVT_HREF - user clicked on a <A HREF> link.  This only applies to
 *   the HTML-enabled run-time. 
 */
#define OS_EVT_HREF      0x0003

/* 
 *   OS_EVT_NOTIMEOUT - caller requested a timeout, but timeout is not
 *   supported by this version of the run-time 
 */
#define OS_EVT_NOTIMEOUT 0x0004

/*
 *   OS_EVT_EOF - an error occurred reading the event.  This generally
 *   means that the application is quitting or we can no longer read from
 *   the keyboard or terminal. 
 */
#define OS_EVT_EOF       0x0005

/* 
 *   OS_EVT_LINE - user entered a line of text on the keyboard.  This event
 *   is not returned from os_get_event(), but rather from os_gets_timeout().
 */
#define OS_EVT_LINE      0x0006


/*
 *   Get an input event.  The event types are shown above.  If use_timeout is
 *   false, this routine should simply wait until one of the events it
 *   recognizes occurs, then return the appropriate information on the event.
 *   If use_timeout is true, this routine should return OS_EVT_TIMEOUT after
 *   the given number of milliseconds elapses if no event occurs first.
 *   
 *   This function is not obligated to obey the timeout.  If a timeout is
 *   specified and it is not possible to obey the timeout, the function
 *   should simply return OS_EVT_NOTIMEOUT.  The trivial implementation thus
 *   checks for a timeout, returns an error if specified, and otherwise
 *   simply waits for the user to press a key.
 *   
 *   A timeout value of 0 does *not* mean that there's no timeout (i.e., it
 *   doesn't mean we should wait indefinitely) - that's specified by passing
 *   FALSE for use_timeout.  A zero timeout also doesn't meant that the
 *   function should unconditionally return OS_EVT_TIMEOUT.  Instead, a zero
 *   timeout specifically means that IF an event is available IMMEDIATELY,
 *   without blocking the thread, we should return that event; otherwise we
 *   should immediately return a timeout event.  
 */
int os_get_event(unsigned long timeout_in_milliseconds, int use_timeout,
                 os_event_info_t *info);


/* ------------------------------------------------------------------------ */
/*
 *   Extended os_get_event() codes.
 *   
 *   THESE ARE NOT USED in the basic osifc implementation - these are only
 *   used if the interpreter supports the "extended" interface defined in
 *   osifcext.h.  
 */

/*
 *   System menu command event.  Some interpreters (such as HTML TADS for
 *   Windows) provide menu commands for common system-level game verbs -
 *   SAVE, RESTORE, UNDO, QUIT.  By default, these commands are returned as
 *   literal command strings ("save", "restore", etc) via os_gets(), as
 *   though the user had typed the commands at the keyboard.  The extended OS
 *   interface allows the program to receive these commands via
 *   os_get_event().  When a command is enabled for os_get_event() via the
 *   extended OS interface, and the player selects the command via a menu (or
 *   toolbar button, etc) during a call to os_get_event(), os_get_event()
 *   returns this event code, with the menu ID stored in the cmd_id field of
 *   the event structure.  
 */
#define OS_EVT_COMMAND   0x0100

/* command IDs for OS_EVT_COMMAND */
#define OS_CMD_NONE      0x0000     /* invalid command ID, for internal use */
#define OS_CMD_SAVE      0x0001                                /* save game */
#define OS_CMD_RESTORE   0x0002                             /* restore game */
#define OS_CMD_UNDO      0x0003                           /* undo last turn */
#define OS_CMD_QUIT      0x0004                                /* quit game */
#define OS_CMD_CLOSE     0x0005                    /* close the game window */
#define OS_CMD_HELP      0x0006                           /* show game help */

/* highest command ID used in this version of the interface */
#define OS_CMD_LAST      0x0006


/* ------------------------------------------------------------------------ */
/*
 *   Ask for input through a dialog.
 *   
 *   'prompt' is a text string to display as a prompting message.  For
 *   graphical systems, this message should be displayed in the dialog;
 *   for text systems, this should be displayed on the terminal after a
 *   newline.
 *   
 *   'standard_button_set' is one of the OS_INDLG_xxx values defined
 *   below, or zero.  If this value is zero, no standard button set is to
 *   be used; the custom set of buttons defined in 'buttons' is to be used
 *   instead.  If this value is non-zero, the appropriate set of standard
 *   buttons, with labels translated to the local language if possible, is
 *   to be used.
 *   
 *   'buttons' is an array of strings to use as button labels.
 *   'button_count' gives the number of entries in the 'buttons' array.
 *   'buttons' and 'button_count' are ignored if 'standard_button_set' is
 *   non-zero, since a standard set of buttons is used instead.  If
 *   'buttons' and 'button_count' are to be used, each entry contains the
 *   label of a button to show.  
 */
/*   
 *   An ampersand ('&') character in a label string indicates that the
 *   next character after the '&' is to be used as the short-cut key for
 *   the button, if supported.  The '&' should NOT be displayed in the
 *   string; instead, the character should be highlighted according to
 *   local system conventions.  On Windows, for example, the short-cut
 *   character should be shown underlined; on a text display, the response
 *   might be shown with the short-cut character enclosed in parentheses.
 *   If there is no local convention for displaying a short-cut character,
 *   then the '&' should simply be removed from the displayed text.  
 *   
 *   The short-cut key specified by each '&' character should be used in
 *   processing responses.  If the user presses the key corresponding to a
 *   button's short-cut, the effect should be the same as if the user
 *   clicked the button with the mouse.  If local system conventions don't
 *   allow for short-cut keys, any short-cut keys can be ignored.
 *   
 *   'default_index' is the 1-based index of the button to use as the
 *   default.  If this value is zero, there is no default response.  If
 *   the user performs the appropriate system-specific action to select
 *   the default response for the dialog, this is the response that is to
 *   be selected.  On Windows, for example, pressing the "Return" key
 *   should select this item.
 *   
 *   'cancel_index' is the 1-based index of the button to use as the
 *   cancel response.  If this value is zero, there is no cancel response.
 *   This is the response to be used if the user cancels the dialog using
 *   the appropriate system-specific action.  On Windows, for example,
 *   pressing the "Escape" key should select this item.  
 */
/*
 *   icon_id is one of the OS_INDLG_ICON_xxx values defined below.  If
 *   possible, an appropriate icon should be displayed in the dialog.
 *   This can be ignored in text mode, and also in GUI mode if there is no
 *   appropriate system icon.
 *   
 *   The return value is the 1-based index of the response selected.  If
 *   an error occurs, return 0.  
 */
int os_input_dialog(int icon_id, const char *prompt, int standard_button_set,
                    const char **buttons, int button_count,
                    int default_index, int cancel_index);

/*
 *   Standard button set ID's 
 */

/* OK */
#define OS_INDLG_OK            1

/* OK, Cancel */
#define OS_INDLG_OKCANCEL      2

/* Yes, No */
#define OS_INDLG_YESNO         3

/* Yes, No, Cancel */
#define OS_INDLG_YESNOCANCEL   4

/*
 *   Dialog icons 
 */

/* no icon */
#define OS_INDLG_ICON_NONE     0

/* warning */
#define OS_INDLG_ICON_WARNING  1

/* information */
#define OS_INDLG_ICON_INFO     2

/* question */
#define OS_INDLG_ICON_QUESTION 3

/* error */
#define OS_INDLG_ICON_ERROR    4



/* ------------------------------------------------------------------------ */
/* 
 *   OS main entrypoint 
 */
int os0main(int oargc, char **oargv,
            int (*mainfn)(int, char **, char *), 
            const char *before, const char *config);

/* 
 *   new-style OS main entrypoint - takes an application container context 
 */
int os0main2(int oargc, char **oargv,
             int (*mainfn)(int, char **, struct appctxdef *, char *),
             const char *before, const char *config,
             struct appctxdef *appctx);

/*
 *   OBSOLETE - Get filename from startup parameter, if possible; returns
 *   true and fills in the buffer with the parameter filename on success,
 *   false if no parameter file could be found.
 *   
 *   (This was used until TADS 2.2.5 for the benefit of the Mac interpreter,
 *   and interpreters on systems with similar desktop shells, to allow the
 *   user to launch the terp by double-clicking on a saved game file.  The
 *   terp would read the launch parameters, discover that a saved game had
 *   been used to invoke it, and would then stash away the saved game info
 *   for later retrieval from this function.  This functionality was replaced
 *   in 2.2.5 with a command-line parameter: the terp now uses the desktop
 *   launch data to synthesize a suitable argv[] vectro to pass to os0main()
 *   or os0main2().  This function should now simply be stubbed out - it
 *   should simply return FALSE.)  
 */
int os_paramfile(char *buf);

/* 
 *   Initialize.  This should be called during program startup to
 *   initialize the OS layer and check OS-specific command-line arguments.
 *   
 *   If 'prompt' and 'buf' are non-null, and there are no arguments on the
 *   given command line, the OS code can use the prompt to ask the user to
 *   supply a filename, then store the filename in 'buf' and set up
 *   argc/argv to give a one-argument command string.  (This mechanism for
 *   prompting for a filename is obsolescent, and is retained for
 *   compatibility with a small number of existing implementations only;
 *   new implementations should ignore this mechanism and leave the
 *   argc/argv values unchanged.)  
 */
int os_init(int *argc, char *argv[], const char *prompt,
            char *buf, int bufsiz);

/*
 *   Termination functions.  There are three main termination functions,
 *   described individually below; here's a brief overview of the
 *   relationship among the functions.  The important thing to realize is
 *   that these functions have completely independent purposes; they should
 *   never call one another, and they should never do any of the work that's
 *   intended for the others.
 *   
 *   os_uninit() is meant to undo the effects of os_init().  On many
 *   systems, os_init() has some global effect, such as setting the terminal
 *   to some special input or output mode.  os_uninit's purpose is to undo
 *   these global effects, returning the terminal mode (and whatever else)
 *   to the conditions they were in at program startup.  Portable callers
 *   are meant to call this routine at some point before terminating if they
 *   ever called os_init().  Note that this routine DOES NOT terminate the
 *   program - it should simply undo anything that os_init() did and return,
 *   to let the caller do any additional termination work of its own.
 *   
 *   os_expause() optionally pauses before termination, to allow the user to
 *   acknowledge any text the program displays just before exiting.  This
 *   doesn't have to do anything at all, but it's useful on systems where
 *   program termination will do something drastic like close the window:
 *   without a pause, the user wouldn't have a chance to read any text the
 *   program displayed after the last interactive input, because the window
 *   would abruptly disappear moments after the text was displayed.  For
 *   systems where termination doesn't have such drastic effects, there's no
 *   need to do anything in this routine.  Because it's up to this routine
 *   whether or not to pause, this routine must display a prompt if it's
 *   going to pause for user input - the portable caller obviously can't do
 *   so, because the caller doesn't know if the routine is going to pause or
 *   not (so if the caller displayed a prompt assuming the routine would
 *   pause, the prompt would show up even on systems where there actually is
 *   no pause, which would be confusing).  This routine DOES NOT terminate
 *   the program; it simply pauses if necessary to allow the user to
 *   acknowledge the last bit of text the program displayed, then returns to
 *   allow the caller to carry on with its own termination work.
 *   
 *   os_term() is meant to perform the same function as the C standard
 *   library routine exit(): this actually terminates the program, exiting
 *   to the operating system.  This routine is not meant to return to its
 *   caller, because it's supposed to exit the program directly.  For many
 *   systems, this routine can simply call exit(); the portable code calls
 *   this routine instead of calling exit() directly, because some systems
 *   have their own OS-specific way of terminating rather than using exit().
 *   This routine MUST NOT undo the effects of os_init(), because callers
 *   might not have ever called os_init(); callers are required to call
 *   os_uninit() if they ever called os_init(), before calling os_term(), so
 *   this routine can simply assume that any global modes set by os_init()
 *   have already been undone by the time this is called.  
 */

/*
 *   Uninitialize.  This is called prior to progam termination to reverse
 *   the effect of any changes made in os_init().  For example, if
 *   os_init() put the terminal in raw mode, this should restore the
 *   previous terminal mode.  This routine should not terminate the
 *   program (so don't call exit() here) - the caller might have more
 *   processing to perform after this routine returns.  
 */
void os_uninit(void);

/* 
 *   Pause prior to exit, if desired.  This is meant to be called by
 *   portable code just before the program is to be terminated; it can be
 *   implemented to show a prompt and wait for user acknowledgment before
 *   proceeding.  This is useful for implementations that are using
 *   something like a character-mode terminal window running on a graphical
 *   operating system: this gives the implementation a chance to pause
 *   before exiting, so that the window doesn't just disappear
 *   unceremoniously.
 *   
 *   This is allowed to do nothing at all.  For regular character-mode
 *   systems, this routine usually doesn't do anything, because when the
 *   program exits, the terminal will simply return to the OS command
 *   prompt; none of the text displayed just before the program exited will
 *   be lost, so there's no need for any interactive pause.  Likewise, for
 *   graphical systems where the window will remain open, even after the
 *   program exits, until the user explicitly closes the window, there's no
 *   need to do anything here.
 *   
 *   If this is implemented to pause, then this routine MUST show some kind
 *   of prompt to let the user know we're waiting.  In the simple case of a
 *   text-mode terminal window on a graphical OS, this should simply print
 *   out some prompt text ("Press a key to exit...") and then wait for the
 *   user to acknowledge the prompt (by pressing a key, for example).  For
 *   graphical systems, the prompt could be placed in the window's title
 *   bar, or status-bar, or wherever is appropriate for the OS.  
 */
void os_expause(void);

/* 
 *   Terminate.  This should exit the program with the given exit status.
 *   In general, this should be equivalent to the standard C library
 *   exit() function, but we define this interface to allow the OS code to
 *   do any necessary pre-termination cleanup.  
 */
void os_term(int status);

/* 
 *   Install/uninstall the break handler.  If possible, the OS code should
 *   set (if 'install' is true) or clear (if 'install' is false) a signal
 *   handler for keyboard break signals (control-C, etc, depending on
 *   local convention).  The OS code should set its own handler routine,
 *   which should note that a break occurred with an internal flag; the
 *   portable code uses os_break() from time to time to poll this flag.  
 */
void os_instbrk(int install);

/*
 *   Check for user break ("control-C", etc) - returns true if a break is
 *   pending, false if not.  If this returns true, it should "consume" the
 *   pending break (probably by simply clearing the OS code's internal
 *   break-pending flag).  
 */
int os_break(void);

/*
 *   Sleep for a given interval.  This should simply pause for the given
 *   number of milliseconds, then return.  On multi-tasking systems, this
 *   should use a system API to suspend the current process for the desired
 *   delay; on single-tasking systems, this can simply sit in a wait loop
 *   until the desired interval has elapsed.  
 */
void os_sleep_ms(long delay_in_milliseconds);

/*
 *   Yield CPU; returns TRUE if user requested an interrupt (a "control-C"
 *   type of operation to abort the entire program), FALSE to continue.
 *   Portable code should call this routine from time to time during lengthy
 *   computations that don't involve any UI operations; if practical, this
 *   routine should be invoked roughly every 10 to 100 milliseconds.
 *   
 *   The purpose of this routine is to support "cooperative multitasking"
 *   systems, such as pre-X MacOS, where it's necessary for each running
 *   program to call the operating system explicitly in order to yield the
 *   CPU from time to time to other concurrently running programs.  On
 *   cooperative multitasking systems, a program can only lose control of
 *   the CPU by making specific system calls, usually related to GUI events;
 *   a program can never lose control of the CPU asynchronously.  So, a
 *   program that performs lengthy computations without any UI interaction
 *   can cause the rest of the system to freeze up until the computations
 *   are finished; but if a compute-intensive program explicitly yields the
 *   CPU from time to time, it allows other programs to remain responsive.
 *   Yielding the CPU at least every 100 milliseconds or so will generally
 *   allow the UI to remain responsive; yielding more frequently than every
 *   10 ms or so will probably start adding noticeable overhead.
 *   
 *   On single-tasking systems (such as MS-DOS), there's only one program
 *   running at a time, so there's no need to yield the CPU; on virtually
 *   every modern system, the OS automatically schedules CPU time without
 *   the running programs having any say in the matter, so again there's no
 *   need for a program to yield the CPU.  For systems where this routine
 *   isn't needed, the system header should simply #define os_yield to
 *   something like "((void)0)" - this will allow the compiler to completely
 *   ignore calls to this routine for systems where they aren't needed.
 *   
 *   Note that this routine is NOT meant to provide scheduling hinting to
 *   modern systems with true multitasking, so a trivial implementation is
 *   fine for any modern system.  
 */
#ifndef os_yield
int os_yield(void);
#endif

/*
 *   Set the default saved-game extension.  This routine will NOT be called
 *   when we're using the standard saved game extension; this routine will be
 *   invoked only if we're running as a stand-alone game, and the game author
 *   specified a non-standard saved-game extension when creating the
 *   stand-alone game.
 *   
 *   This routine is not required if the system does not use the standard,
 *   semi-portable os0.c implementation.  Even if the system uses the
 *   standard os0.c implementation, it can provide an empty routine here if
 *   the system code doesn't need to do anything special with this
 *   information.
 *   
 *   The extension is specified as a null-terminated string.  The extension
 *   does NOT include the leading period.  
 */
void os_set_save_ext(const char *ext);

/* 
 *   Get the saved game extension previously set with os_set_save_ext().
 *   Returns null if no custom extension has been set.
 */
const char *os_get_save_ext();


/* ------------------------------------------------------------------------*/
/*
 *   Translate a character from the HTML 4 Unicode character set to the
 *   current character set used for display.  Takes an HTML 4 character
 *   code and returns the appropriate local character code.
 *   
 *   The result buffer should be filled in with a null-terminated string
 *   that should be used to represent the character.  Multi-character
 *   results are possible, which may be useful for certain approximations
 *   (such as using "(c)" for the copyright symbol).
 *   
 *   Note that we only define this prototype if this symbol isn't already
 *   defined as a macro, which may be the case on some platforms.
 *   Alternatively, if the function is already defined (for example, as an
 *   inline function), the defining code can define OS_XLAT_HTML4_DEFINED,
 *   in which case we'll also omit this prototype.
 *   
 *   Important: this routine provides the *default* mapping that is used
 *   when no external character mapping file is present, and for any named
 *   entities not defined in the mapping file.  Any entities in the
 *   mapping file, if used, will override this routine.
 *   
 *   A trivial implementation of this routine (that simply returns a
 *   one-character result consisting of the original input character,
 *   truncated to eight bits if necessary) can be used if you want to
 *   require an external mapping file to be used for any game that
 *   includes HTML character entities.  The DOS version implements this
 *   routine so that games will still look reasonable when played with no
 *   mapping file present, but other systems are not required to do this.  
 */
#ifndef os_xlat_html4
# ifndef OS_XLAT_HTML4_DEFINED
void os_xlat_html4(unsigned int html4_char,
                   char *result, size_t result_buf_len);
# endif
#endif

/*
 *   Generate a filename for a character-set mapping file.  This function
 *   should determine the current native character set in use, if
 *   possible, then generate a filename, according to system-specific
 *   conventions, that we should attempt to load to get a mapping between
 *   the current native character set and the internal character set
 *   identified by 'internal_id'.
 *   
 *   The internal character set ID is a string of up to 4 characters.
 *   
 *   On DOS, the native character set is a DOS code page.  DOS code pages
 *   are identified by 3- or 4-digit identifiers; for example, code page
 *   437 is the default US ASCII DOS code page.  We generate the
 *   character-set mapping filename by appending the internal character
 *   set identifier to the DOS code page number, then appending ".TCP" to
 *   the result.  So, to map between ISO Latin-1 (internal ID = "La1") and
 *   DOS code page 437, we would generate the filename "437La1.TCP".
 *   
 *   Note that this function should do only two things.  First, determine
 *   the current native character set that's in use.  Second, generate a
 *   filename based on the current native code page and the internal ID.
 *   This function is NOT responsible for figuring out the mapping or
 *   anything like that -- it's simply where we generate the correct
 *   filename based on local convention.
 *   
 *   'filename' is a buffer of at least OSFNMAX characters.
 *   
 *   'argv0' is the executable filename from the original command line.
 *   This parameter is provided so that the system code can look for
 *   mapping files in the original TADS executables directory, if desired.
 */
void os_gen_charmap_filename(char *filename, char *internal_id,
                             char *argv0);

/*
 *   Receive notification that a character mapping file has been loaded.
 *   The caller doesn't require this routine to do anything at all; this
 *   is purely for the system-dependent code's use so that it can take
 *   care of any initialization that it must do after the caller has
 *   loaded a charater mapping file.  'id' is the character set ID, and
 *   'ldesc' is the display name of the character set.  'sysinfo' is the
 *   extra system information string that is stored in the mapping file;
 *   the interpretation of this information is up to this routine.
 *   
 *   For reference, the Windows version uses the extra information as a
 *   code page identifier, and chooses its default font character set to
 *   match the code page.  On DOS, the run-time requires the player to
 *   activate an appropriate code page using a DOS command (MODE CON CP
 *   SELECT) prior to starting the run-time, so this routine doesn't do
 *   anything at all on DOS. 
 */
void os_advise_load_charmap(const char *id, const char *ldesc, const char *sysinfo);

/*
 *   Generate the name of the character set mapping table for Unicode
 *   characters to and from the given local character set.  Fills in the
 *   buffer with the implementation-dependent name of the desired
 *   character set map.  See below for the character set ID codes.
 *   
 *   For example, on Windows, the implementation would obtain the
 *   appropriate active code page (which is simply a Windows character set
 *   identifier number) from the operating system, and build the name of
 *   the Unicode mapping file for that code page, such as "CP1252".  On
 *   Macintosh, the implementation would look up the current script system
 *   and return the name of the Unicode mapping for that script system,
 *   such as "ROMAN" or "CENTEURO".
 *   
 *   If it is not possible to determine the specific character set that is
 *   in use, this function should return "asc7dflt" (ASCII 7-bit default)
 *   as the character set identifier on an ASCII system, or an appropriate
 *   base character set name on a non-ASCII system.  "asc7dflt" is the
 *   generic character set mapping for plain ASCII characters.
 *   
 *   The given buffer must be at least 32 bytes long; the implementation
 *   must limit the result it stores to 32 bytes.  (We use a fixed-size
 *   buffer in this interface for simplicity, and because there seems no
 *   need for greater flexibility in the interface; a character set name
 *   doesn't carry very much information so shouldn't need to be very
 *   long.  Note that this function doesn't generate a filename, but
 *   simply a mapping name; in practice, a map name will be used to
 *   construct a mapping file name.)
 *   
 *   Because this function obtains the Unicode mapping name, there is no
 *   need to specify the internal character set to be used: the internal
 *   character set is Unicode.  
 */
/*
 *   Implementation note: when porting this routine, the convention that
 *   you use to name your mapping files is up to you.  You should simply
 *   choose a convention for this implementation, and then use the same
 *   convention for packaging the mapping files for your OS release.  In
 *   most cases, the best convention is to use the names that the Unicode
 *   consortium uses in their published cross-mapping listings, since
 *   these listings can be used as the basis of the mapping files that you
 *   include with your release.  For example, on Windows, the convention
 *   is to use the code page number to construct the map name, as in
 *   CP1252 or CP1250.  
 */
void os_get_charmap(char *mapname, int charmap_id);

/*
 *   Character map for the display (i.e., for the user interface).  This
 *   is the character set which is used for input read from the keyboard,
 *   and for output displayed on the monitor or terminal.  
 */
#define OS_CHARMAP_DISPLAY     1

/* 
 *   Character map for mapping filename strings.  This should identify the
 *   character set currently in use on the local system for filenames
 *   (i.e., for strings identifying objects in the local file system),
 *   providing a suitable name for use in opening a mapping file.
 *   
 *   On many platforms, the entire system uses only one character set at
 *   the OS level, so the file system character set is the same as the
 *   display character set.  Some systems define a particular character
 *   set for file system use, though, because different users might be
 *   running applications on terminals that display different character
 *   sets.  
 */
#define OS_CHARMAP_FILENAME    2

/*
 *   Default character map for file contents.  On most systems, this will
 *   be the same as display.  On some systems, it won't be possible to
 *   know in general what character set files use; in fact, this isn't
 *   possible anywhere, since a file might have been copied without
 *   translation from a different type of computer using a different
 *   character set.  So, this isn't meant to provide a reliable choice of
 *   character set for any arbitrary file; it's simply meant to be a good
 *   guess that most files on this system are likely to use.  
 */
#define OS_CHARMAP_FILECONTENTS  3

/*
 *   Default character map for the command line.  This is the maping we use
 *   to interpret command line arguments passed to our main() or equivalent.
 *   On most systems, this will be the same as the display character set.
 */
#define OS_CHARMAP_CMDLINE     4


/* ------------------------------------------------------------------------ */
/*
 *   External Banner Interface.  This interface provides the ability to
 *   divide the display window into multiple sub-windows, each with its own
 *   independent contents.
 *   
 *   To determine where a new banner is displayed, we look at the banners as
 *   a tree, rooted at the "main window," the special banner that the system
 *   automatically creates initially for the main game text.  We start by
 *   allocating the entire display (or the entire application window, if
 *   we're running on a GUI system) to the main window.  We then traverse
 *   the tree, starting with the root window's children.  For each child
 *   window, we allocate space for the child out of the parent window's
 *   area, according to the child's alignment and size settings, and deduct
 *   this space from the parent window's size.  We then lay out the children
 *   of the child.
 *   
 *   For each banner window, we take its requested space out of the parent
 *   window's area by starting at the edge of the parent window rectangle as
 *   indicated by the banner's alignment, and taking the requested `width
 *   (for a left/right banner) or height (for a top/bottom banner), limiting
 *   to the available width/height in the parent window's space.  Give the
 *   banner the full extent of the parent's space in its other dimension (so
 *   a left/right banner gets the full height of the parent space, and a
 *   top/bottom banner gets the full width).
 *   
 *   Note that the layout proceeds exclusively down the tree (i.e., from the
 *   root to children to grandchildren, and so on).  It *appears* that a
 *   child affects its parent, because of the deduction step: a child
 *   acquires screen space by carving out a chunk of its parent.  The right
 *   way to think about this, though, is that the parent's full area is the
 *   union of the parent window and all of its children; when viewed this
 *   way, the parent's full area is fully determined the instant the parent
 *   is laid out, and never changes as its children are laid out.  Note in
 *   particular that a child can never make a parent larger; the only thing
 *   a child can do to a parent is carve out a chunk of the parent for
 *   itself, which doesn't affect the boundaries of the union of the parent
 *   plus its children.
 *   
 *   Note also that if the banner has a border, and the implementation
 *   actually draws borders, the border must be drawn for the *full* area of
 *   the banner, as defined above.  For example, suppose we have two
 *   borders: banner A is a child of the main window, is top-aligned, and
 *   has a border.  Banner B is a child of banner A, right-aligned, with no
 *   border.  Obviously, without considering banner B, banner A's space runs
 *   across the entire width of the main window, so its border (at the
 *   bottom of its area) runs across the entire width of the main window.
 *   Banner B carves out some space from A's right side for itself, so
 *   banner A's actual on-screen area runs from the left edge of the main
 *   window to banner B's left edge.  However, even though banner A itself
 *   no longer runs the full width of the main window, banner A's *full*
 *   area - that is, the union of banner A's on-screen area and all of its
 *   children's full areas - does still run the entire width of the main
 *   window, hence banner A's border must still run the full width of the
 *   main window.  The simple way of looking at this is that a banner's
 *   border is always to be drawn exactly the same way, regardless of
 *   whether or not the banner has children - simply draw the banner as it
 *   would be drawn if the banner had no children.
 *   
 *   Each time a banner is added or removed, we must recalculate the layout
 *   of the remaining banners and main text area.  The os_banner_xxx()
 *   implementation is responsible for this layout refiguring.
 *   
 *   The entire external banner window interface is optional, although the
 *   functions must at least be defined as dummies to avoid linker errors
 *   when building.  If a platform doesn't implement this feature,
 *   os_banner_create() should simply return null, and the other routines
 *   can do nothing.  
 */

/* 
 *   Create a banner window.  'info' gives the desired parameters for the new
 *   banner.
 *   
 *   Note that certain requested parameter settings might or might not be
 *   respected, depending on the capabilities of the platform and user
 *   preferences.  os_banner_getinfo() can be used after creation to
 *   determine which parameter settings are actually used in the new banner.
 *   
 *   'parent' gives the parent of this banner; this is the banner handle of
 *   another banner window, or null.  If 'parent' is null, then the new
 *   banner is a child of the main window, which the system creates
 *   automatically at startup and which contains the main input/output
 *   transcript.  The new banner's on-screen area is carved out of the
 *   parent's space, according to the alignment and size settings of the new
 *   window, so this determines how the window is laid out on the screen.
 *   
 *   'where' is OS_BANNER_FIRST to make the new window the first child of its
 *   parent; OS_BANNER_LAST to make it the last child of its parent;
 *   OS_BANNER_BEFORE to insert it immediately before the existing banner
 *   identified by handle in 'other'; or OS_BANNER_AFTER to insert
 *   immediately after 'other'.  When BEFORE or AFTER is used, 'other' must
 *   be another child of the same parent; if it is not, the routine should
 *   act as though 'where' were given as OS_BANNER_LAST.
 *   
 *   'other' is a banner handle for an existing banner window.  This is used
 *   to specify the relative position among children of the new banner's
 *   parent, if 'where' is either OS_BANNER_BEFORE or OS_BANNER_AFTER.  If
 *   'where' is OS_BANNER_FIRST or OS_BANNER_LAST, 'other' is ignored.
 *   
 *   'wintype' is the type of the window.  This is one of the
 *   OS_BANNER_TYPE_xxx codes indicating what kind of window is desired.
 *   
 *   'align' is the banner's alignment, given as an OS_BANNER_ALIGN_xxx
 *   value.  Top/bottom banners are horizontal: they run across the full
 *   width of the existing main text area.  Left/right banners are vertical:
 *   they run down the full height of the existing main text area.
 *   
 *   'siz' is the requested size of the new banner.  The meaning of 'siz'
 *   depends on the value of 'siz_units', which can be OS_BANNER_SIZE_PCT to
 *   set the size as a percentage of the REMAINING space, or
 *   OS_BANNER_SIZE_ABS to set an absolute size in the "natural" units of the
 *   window.  The natural units vary by window type: for text and text grid
 *   windows, this is in rows/columns of '0' characters in the default font
 *   for the window.  Note that when OS_BANNER_SIZE_ABS is used in a text or
 *   text grid window, the OS implementation MUST add the space needed for
 *   margins and borders when determining the actual pixel size of the
 *   window; in other words, the window should be large enough that it can
 *   actually display the given number or rows or columns.
 *   
 *   The size is interpreted as a width or height according to the window's
 *   orientation.  For a TOP or BOTTOM banner, the size is the height; for a
 *   LEFT or RIGHT banner, the size is the width.  A banner has only one
 *   dimension's size given, since the other dimension's size is determined
 *   automatically by the layout rules.
 *   
 *   Note that the window's size can be changed later using
 *   banner_size_to_contents() or banner_set_size().
 *   
 *   'style' is a combination of OS_BANNER_STYLE_xxx flags - see below.  The
 *   style flags give the REQUESTED style for the banner, which might or
 *   might not be respected, depending on the platform's capabilities, user
 *   preferences, and other factors.  os_banner_getinfo() can be used to
 *   determine which style flags are actually used.
 *   
 *   Returns the "handle" to the new banner window, which is an opaque value
 *   that is used in subsequent os_banner_xxx calls to operate on the window.
 *   Returns null if the window cannot be created.  An implementation is not
 *   required to support this functionality at all, and can subset it if it
 *   does support it (for example, an implementation could support only
 *   top/bottom-aligned banners, but not left/right-aligned), so callers must
 *   be prepared for this routine to return null.  
 */
void *os_banner_create(void *parent, int where, void *other, int wintype,
                       int align, int siz, int siz_units,
                       unsigned long style);


/*
 *   insertion positions 
 */
#define OS_BANNER_FIRST   1
#define OS_BANNER_LAST    2
#define OS_BANNER_BEFORE  3
#define OS_BANNER_AFTER   4

/*
 *   banner types 
 */

/* 
 *   Normal text stream window.  This is a text stream that behaves
 *   essentially like the main text window: text is displayed to this
 *   through os_banner_disp(), always in a stream-like fashion by adding new
 *   text to the end of any exiting text.
 *   
 *   Systems that use proportional fonts should usually simply use the same
 *   font they use by default in the main text window.  However, note that
 *   the OS_BANNER_STYLE_TAB_ALIGN style flag might imply that a fixed-pitch
 *   font should be used even when proportional fonts are available, because
 *   a fixed-pitch font will allow the calling code to rely on using spaces
 *   to align text within the window.  
 */
#define OS_BANNER_TYPE_TEXT       1

/* 
 *   "Text grid" window.  This type of window is similar to an normal text
 *   window (OS_BANNER_TYPE_TEXT), but is guaranteed to arrange its text in
 *   a regular grid of character cells, all of the same size.  This means
 *   that the output position can be moved to an arbitrary point within the
 *   window at any time, so the calling program can precisely control the
 *   layout of the text in the window.
 *   
 *   Because the output position can be moved to arbitrary positions in the
 *   window, it is possible to overwrite text previously displayed.  When
 *   this happens, the old text is completely obliterated by the new text,
 *   leaving no trace of the overwritten text.
 *   
 *   In order to guarantee that character cells are all the same size, this
 *   type of window does not allow any text attributes.  The implementation
 *   should simply ignore any attempts to change text attributes in this
 *   type of window.  However, colors can be used to the same degree they
 *   can be used in an ordinary text window.
 *   
 *   To guarantee the regular spacing of character cells, all
 *   implementations must use fixed-pitch fonts for these windows.  This
 *   applies even to platforms where proportional fonts are available.  
 */
#define OS_BANNER_TYPE_TEXTGRID   2


/* 
 *   banner alignment types 
 */
#define OS_BANNER_ALIGN_TOP       0
#define OS_BANNER_ALIGN_BOTTOM    1
#define OS_BANNER_ALIGN_LEFT      2
#define OS_BANNER_ALIGN_RIGHT     3

/*
 *   size units 
 */
#define OS_BANNER_SIZE_PCT  1
#define OS_BANNER_SIZE_ABS  2


/* 
 *   banner style flags 
 */

/* 
 *   The banner has a visible border; this indicates that a line is to be
 *   drawn to separate the banner from the adjacent window or windows
 *   "inside" the banner.  So, a top-aligned banner will have its border
 *   drawn along its bottom edge; a left-aligned banner will show a border
 *   along its right edge; and so forth.
 *   
 *   Note that character-mode platforms generally do NOT respect the border
 *   style, since doing so takes up too much screen space.  
 */
#define OS_BANNER_STYLE_BORDER     0x00000001

/*
 *   The banner has a vertical/horizontal scrollbar.  Character-mode
 *   platforms generally do not support scrollbars.  
 */
#define OS_BANNER_STYLE_VSCROLL    0x00000002
#define OS_BANNER_STYLE_HSCROLL    0x00000004

/* 
 *   Automatically scroll the banner vertically/horizontally whenever new
 *   text is displayed in the window.  In other words, whenever
 *   os_banner_disp() is called, scroll the window so that the text that the
 *   new cursor position after the new text is displayed is visible in the
 *   window.
 *   
 *   Note that this style is independent of the presence of scrollbars.
 *   Even if there are no scrollbars, we can still scroll the window's
 *   contents programmatically.
 *   
 *   Implementations can, if desired, keep an internal buffer of the
 *   window's contents, so that the contents can be recalled via the
 *   scrollbars if the text displayed in the banner exceeds the space
 *   available in the banner's window on the screen.  If the implementation
 *   does keep such a buffer, we recommend the following method for managing
 *   this buffer.  If the AUTO_VSCROLL flag is not set, then the banner's
 *   contents should be truncated at the bottom when the contents overflow
 *   the buffer; that is, once the banner's internal buffer is full, any new
 *   text that the calling program attempts to add to the banner should
 *   simply be discarded.  If the AUTO_VSCROLL flag is set, then the OLDEST
 *   text should be discarded instead, so that the most recent text is
 *   always retained.  
 */
#define OS_BANNER_STYLE_AUTO_VSCROLL 0x00000008
#define OS_BANNER_STYLE_AUTO_HSCROLL 0x00000010

/*
 *   Tab-based alignment is required/supported.  On creation, this is a hint
 *   to the implementation that is sometimes necessary to determine what
 *   kind of font to use in the new window, for non-HTML platforms.  If this
 *   flag is set on creation, the caller is indicating that it wants to use
 *   <TAB> tags to align text in the window.
 *   
 *   Character-mode implementations that use a single font with fixed pitch
 *   can simply ignore this.  These implementations ALWAYS have a working
 *   <TAB> capability, because the portable output formatter provides <TAB>
 *   interpretation for a fixed-pitch window.
 *   
 *   Full HTML TADS implementations can also ignore this.  HTML TADS
 *   implementations always have full <TAB> support via the HTML
 *   parser/renderer.
 *   
 *   Text-only implementations on GUI platforms (i.e., implementations that
 *   are not based on the HTML parser/renderer engine in HTML TADS, but
 *   which run on GUI platforms with proportionally-spaced text) should use
 *   this flag to determine the font to display.  If this flag is NOT set,
 *   then the caller doesn't care about <TAB>, and the implementation is
 *   free to use a proportionally-spaced font in the window if desired.
 *   
 *   When retrieving information on an existing banner, this flag indicates
 *   that <TAB> alignment is actually supported on the window.  
 */
#define OS_BANNER_STYLE_TAB_ALIGN 0x00000020

/*
 *   Use "MORE" mode in this window.  By default, a banner window should
 *   happily allow text to overflow the vertical limits of the window; the
 *   only special thing that should happen on overflow is that the window
 *   should be srolled down to show the latest text, if the auto-vscroll
 *   style is set.  With this flag, though, a banner window acts just like
 *   the main text window: when the window fills up vertically, we show a
 *   MORE prompt (using appropriate system conventions), and wait for the
 *   user to indicate that they're ready to see more text.  On most systems,
 *   the user acknowledges a MORE prompt by pressing a key or scrolling with
 *   the mouse, but it's up to the system implementor to decide what's
 *   appropriate for the system.
 *   
 *   Note that MORE mode in ANY banner window should generally override all
 *   other user input focus.  In other words, if the game in the main window
 *   would like to read a keystroke from the user, but one of the banner
 *   windows is pausing with a MORE prompt, any keyboard input should be
 *   directed to the banner paused at the MORE prompt, not to the main
 *   window; the main window should not receive any key events until the MORE
 *   prompt has been removed.
 *   
 *   This style requires the auto-vscroll style.  Implementations should
 *   assume auto-vscroll when this style is set.  This style can be ignored
 *   with text grid windows.  
 */
#define OS_BANNER_STYLE_MOREMODE  0x00000040

/*
 *   This banner is a horizontal/vertical "strut" for sizing purposes.  This
 *   means that the banner's content size is taken into account when figuring
 *   the content size of its *parent* banner.  If the banner has the same
 *   orientation as the parent, its content size is added to its parent's
 *   internal content size to determine the parent's overall content size.
 *   If the banner's orientation is orthogonal to the parent's, then the
 *   parent's overall content size is the larger of the parent's internal
 *   content size and this banner's content size.  
 */
#define OS_BANNER_STYLE_HSTRUT    0x00000080
#define OS_BANNER_STYLE_VSTRUT    0x00000100


/* 
 *   Delete a banner.  This removes the banner from the display, which
 *   requires recalculating the entire screen's layout to reallocate this
 *   banner's space to other windows.  When this routine returns, the banner
 *   handle is invalid and can no longer be used in any os_banner_xxx
 *   function calls.  
 *   
 *   If the banner has children, the children will no longer be displayed,
 *   but will remain valid in memory until deleted.  A child window's
 *   display area always comes out of its parent's space, so once the parent
 *   is gone, a child has no way to acquire any display space; resizing the
 *   child won't help, since it simply has no way to obtain any screen space
 *   once its parent has been deleted.  Even though the window's children
 *   will become invisible, their banner handles will remain valid; the
 *   caller is responsible for explicitly deleting the children even after
 *   deleting their parent.  
 */
void os_banner_delete(void *banner_handle);

/*
 *   "Orphan" a banner.  This tells the osifc implementation that the caller
 *   wishes to sever all of its ties with the banner (as part of program
 *   termination, for example), but that the calling program does not
 *   actually require that the banner's on-screen display be immediately
 *   removed.
 *   
 *   The osifc implementation can do one of two things:
 *   
 *   1.  Simply call os_banner_delete().  If the osifc implementation
 *   doesn't want to do anything extra with the banner, it can simply delete
 *   the banner, since the caller has no more use for it.
 *   
 *   2.  Take ownership of the banner.  If the osifc implementation wishes
 *   to continue displaying the final screen configuration after a program
 *   has terminated, it can simply take over the banner and leave it on the
 *   screen.  The osifc subsystem must eventually delete the banner itself
 *   if it takes this routine; for example, if the osifc subsystem allows
 *   another client program to be loaded into the same window after a
 *   previous program has terminated, it would want to delete any orphaned
 *   banners from the previous program when loading a new program.  
 */
void os_banner_orphan(void *banner_handle);

/*
 *   Banner information structure.  This is filled in by the system-specific
 *   implementation in os_banner_getinfo().  
 */
struct os_banner_info_t
{
    /* alignment */
    int align;

    /* style flags - these indicate the style flags actually in use */
    unsigned long style;

    /* 
     *   Actual on-screen size of the banner, in rows and columns.  If the
     *   banner is displayed in a proportional font or can display multiple
     *   fonts of different sizes, this is approximated by the number of "0"
     *   characters in the window's default font that will fit in the
     *   window's display area.  
     */
    int rows;
    int columns;

    /*
     *   Actual on-screen size of the banner in pixels.  This is meaningful
     *   only for full HTML interpreter; for text-only interpreters, these
     *   are always set to zero.
     *   
     *   Note that even if we're running on a GUI operating system, these
     *   aren't meaningful unless this is a full HTML interpreter.  Text-only
     *   interpreters should always set these to zero, even on GUI OS's.  
     */
    int pix_width;
    int pix_height;

    /* 
     *   OS line wrapping flag.  If this is set, the window uses OS-level
     *   line wrapping because the window uses a proportional font, so the
     *   caller does not need to (and should not) perform line breaking in
     *   text displayed in the window.
     *   
     *   Note that OS line wrapping is a PERMANENT feature of the window.
     *   Callers can note this information once and expect it to remain
     *   fixed through the window's lifetime.  
     */
    int os_line_wrap;
};
typedef struct os_banner_info_t os_banner_info_t;

/* 
 *   Get information on the banner - fills in the information structure with
 *   the banner's current settings.  Note that this should indicate the
 *   ACTUAL properties of the banner, not the requested properties; this
 *   allows callers to determine how the banner is actually displayed, which
 *   depends upon the platform's capabilities and user preferences.
 *   
 *   Returns true if the information was successfully obtained, false if
 *   not.  This can return false if the underlying OS window has already
 *   been closed by a user action, for example.  
 */
int os_banner_getinfo(void *banner_handle, os_banner_info_t *info);

/* 
 *   Get the character width/height of the banner, for layout purposes.  This
 *   gives the size of the banner in character cells.
 *   
 *   These are not meaningful when the underlying window uses a proportional
 *   font or varying fonts of different sizes.  When the size of text varies
 *   in the window, the OS layer is responsible for word-wrapping and other
 *   layout, in which case these simply return zero.
 *   
 *   Note that these routines might appear to be redundant with the 'rows'
 *   and 'columns' information returned from os_banner_getinfo(), but these
 *   have two important distinctions.  First, these routines return only the
 *   width and height information, so they can be implemented with less
 *   overhead than os_banner_getinfo(); this is important because formatters
 *   might need to call these routines frequently while formatting text.
 *   Second, these routines are not required to return an approximation for
 *   windows using proportional fonts, as os_banner_getinfo() does; these can
 *   simply return zero when a proportional font is in use.  
 */
int os_banner_get_charwidth(void *banner_handle);
int os_banner_get_charheight(void *banner_handle);

/* clear the contents of a banner */
void os_banner_clear(void *banner_handle);

/* 
 *   Display output on a banner.  Writes the output to the window on the
 *   display at the current output position.
 *   
 *   The following special characters should be recognized and handled:
 *   
 *   '\n' - newline; move output position to the start of the next line.
 *   
 *   '\r' - move output position to start of current line; subsequent text
 *   overwrites any text previously displayed on the current line.  It is
 *   permissible to delete the old text immediately on seeing the '\r',
 *   rather than waiting for additional text to actually overwrite it.
 *   
 *   All other characters should simply be displayed as ordinary printing
 *   text characters.  Note that tab characters should not be passed to this
 *   routine, but if they are, they can simply be treated as ordinary spaces
 *   if desired.  Other control characters (backspace, escape, etc) should
 *   never be passed to this routine; the implementation is free to ignore
 *   any control characters not listed above.
 *   
 *   If any text displayed here overflows the current boundaries of the
 *   window on the screen, the text MUST be "clipped" to the current window
 *   boundaries; in other words, anything this routine tries to display
 *   outside of the window's on-screen rectangle must not actually be shown
 *   on the screen.
 *   
 *   Text overflowing the display boundaries MUST also be retained in an
 *   internal buffer.  This internal buffer can be limited to the actual
 *   maximum display size of the terminal screen or application window, if
 *   desired.  It is necessary to retain clipped text, because this allows a
 *   window to be expanded to the size of its contents AFTER the contents
 *   have already been displayed.
 *   
 *   If the banner does its own line wrapping, it must indicate this via the
 *   os_line_wrap flag in the os_banner_getinfo() return data.  If the
 *   banner doesn't indicate this flag, then it must not do any line
 *   wrapping at all, even if the caller attempts to write text beyond the
 *   right edge of the window - any text overflowing the width of the window
 *   must simply be clipped.
 *   
 *   Text grid banners must ALWAYS clip - these banners should never perform
 *   any line wrapping.  
 */
void os_banner_disp(void *banner_handle, const char *txt, size_t len);

/*
 *   Set the text attributes in a banner, for subsequent text displays.
 *   'attr' is a (bitwise-OR'd) combination of OS_ATTR_xxx values. 
 */
void os_banner_set_attr(void *banner_handle, int attr);

/* 
 *   Set the text color in a banner, for subsequent text displays.  The 'fg'
 *   and 'bg' colors are given as RGB or parameterized colors; see the
 *   definition of os_color_t for details.
 *   
 *   If the underlying renderer is HTML-enabled, then this should not be
 *   used; the appropriate HTML code should simply be displayed to the
 *   banner instead.  
 */
void os_banner_set_color(void *banner_handle, os_color_t fg, os_color_t bg);

/* 
 *   Set the screen color in the banner - this is analogous to the screen
 *   color in the main text area.
 *   
 *   If the underlying renderer is HTML-enabled, then this should not be
 *   used; the HTML <BODY> tag should be used instead.  
 */
void os_banner_set_screen_color(void *banner_handle, os_color_t color);

/* flush output on a banner */
void os_banner_flush(void *banner_handle);

/*
 *   Set the banner's size.  The size has the same meaning as in
 *   os_banner_create().
 *   
 *   'is_advisory' indicates whether the sizing is required or advisory only.
 *   If this flag is false, then the size should be set as requested.  If
 *   this flag is true, it means that the caller intends to call
 *   os_banner_size_to_contents() at some point, and that the size being set
 *   now is for advisory purposes only.  Platforms that support
 *   size-to-contents may simply ignore advisory sizing requests, although
 *   they might want to ensure that they have sufficient off-screen buffer
 *   space to keep track of the requested size of display, so that the
 *   information the caller displays in preparation for calling
 *   size-to-contents will be retained.  Platforms that do not support
 *   size-to-contents should set the requested size even when 'is_advisory'
 *   is true.  
 */
void os_banner_set_size(void *banner_handle, int siz, int siz_units,
                        int is_advisory);

/* 
 *   Set the banner to the size of its current contents.  This can be used
 *   to set the banner's size after some text (or other material) has been
 *   displayed to the banner, so that the size can be set according to the
 *   banner's actual space requirements.
 *   
 *   This changes the banner's "requested size" to match the current size.
 *   Subsequent calls to os_banner_getinfo() will thus indicate a requested
 *   size according to the size set here.  
 */
void os_banner_size_to_contents(void *banner_handle);

/* 
 *   Turn HTML mode on/off in the banner window.  If the underlying renderer
 *   doesn't support HTML, these have no effect.  
 */
void os_banner_start_html(void *banner_handle);
void os_banner_end_html(void *banner_handle);

/*
 *   Set the output coordinates in a text grid window.  The grid window is
 *   arranged into character cells numbered from row zero, column zero for
 *   the upper left cell.  This function can only be used if the window was
 *   created with type OS_BANNER_TYPE_TEXTGRID; the request should simply be
 *   ignored by other window types.
 *   
 *   Moving the output position has no immediate effect on the display, and
 *   does not itself affect the "content size" for the purposes of
 *   os_banner_size_to_contents().  This simply sets the coordinates where
 *   any subsequent text is displayed.  
 */
void os_banner_goto(void *banner_handle, int row, int col);


/* ------------------------------------------------------------------------ */
/*
 *   Get system information.  'code' is a SYSINFO_xxx code, which
 *   specifies what type of information to get.  The 'param' argument's
 *   meaning depends on which code is selected.  'result' is a pointer to
 *   an integer that is to be filled in with the result value.  If the
 *   code is not known, this function should return FALSE.  If the code is
 *   known, the function should fill in *result and return TRUE.
 */
int os_get_sysinfo(int code, void *param, long *result);

/* determine if systemInfo is supported - os_get_sysinfo never gets this */
#define SYSINFO_SYSINFO   1

/* get interpreter version number - os_get_sysinfo never gets this */
#define SYSINFO_VERSION   2

/* get operating system name - os_get_sysinfo never gets this */
#define SYSINFO_OS_NAME   3

/* 
 *   Can the system process HTML directives?  returns 1 if so, 0 if not.
 *   Note that if this returns false, then all of the codes below from
 *   JPEG to LINKS are implicitly false as well, since TADS can only use
 *   images, sounds, and links through HTML. 
 */
#define SYSINFO_HTML      4

/* can the system display JPEG's?  1 if yes, 0 if no */
#define SYSINFO_JPEG      5

/* can the system display PNG's?  1 if yes, 0 if no */
#define SYSINFO_PNG       6

/* can the system play WAV's?  1 if yes, 0 if no */
#define SYSINFO_WAV       7

/* can the system play MIDI's?  1 if yes, 0 if no */
#define SYSINFO_MIDI      8

/* can the system play MIDI and WAV's simultaneously?  yes=1, no=0 */
#define SYSINFO_WAV_MIDI_OVL  9

/* can the system play multiple WAV's simultaneously?  yes=1, no=0 */
#define SYSINFO_WAV_OVL   10

/*
 *   GENERAL NOTES ON PREFERENCE SETTINGS:
 *   
 *   Several of the selectors below refer to the preference settings.  We're
 *   talking about user-settable options to control various aspects of the
 *   interpreter.  The conventional GUI for this kind of thing is a dialog
 *   box reachable through a menu command named something like "Options" or
 *   "Preferences".  A couple of general notes about these:
 *   
 *   1.  The entire existence of a preferences mechanism is optional -
 *   interpreter writers aren't required to implement anything along these
 *   lines.  In some cases the local platforms might not have any suitable
 *   conventions for a preferences UI (e.g., character-mode console
 *   applications), and in other cases the terp developer might just want to
 *   omit a prefs mechanism because of the work involved to implement it, or
 *   to keep the UI simpler.
 *   
 *   2.  If a given SYSINFO_PREF_xxx selector refers to a preference item
 *   that's not implemented in the local interpreter, the terp should simply
 *   return a suitable default result.  For example, if the interpreter
 *   doesn't have a preference item to allow the user to turn sounds off, the
 *   selector SYSINFO_PREF_SOUNDS should return 1 to indicate that the user
 *   has not in fact turned off sounds (because there's no way to do so).
 *   
 *   3.  The various SYSINFO_PREF_xxx selectors are purely queries - they're
 *   NOT a mechanism for enforcing the preferences.  For example, if the
 *   interpreter provides a "Sounds On/Off" option, it's up to the terp to
 *   enforce it the Off setting by ignoring any sound playback requests.  The
 *   game isn't under any obligation to query any of the preferences or to
 *   alter its behavior based on their settings - you should expect that the
 *   game will go on trying to play sounds even when "Sounds Off" is selected
 *   in the preferences.  The purpose of these SYSINFO selectors is to let
 *   the game determine the current presentation status, but *only if it
 *   cares*.  For example, the game might determine whether or not sounds are
 *   actually being heard just before playing a sound effect that's important
 *   to the progress of the game, so that it can provide a visual alternative
 *   if the effect won't be heard.  
 */

/* 
 *   Get image preference setting - 1 = images can be displayed, 0 = images
 *   are not being displayed because the user turned off images in the
 *   preferences.  This is, of course, irrelevant if images can't be
 *   displayed at all.
 *   
 *   See the general notes on preferences queries above.  
 */
#define SYSINFO_PREF_IMAGES  11

/*
 *   Get digitized sound effect (WAV) preference setting - 1 = sounds can be
 *   played, 0 = sounds are not being played because the user turned off
 *   sounds in the preferences.
 *   
 *   See the general notes on preferences queries above.  
 */
#define SYSINFO_PREF_SOUNDS  12

/*
 *   Get music (MIDI) preference setting - 1 = music can be played, 0 = music
 *   is not being played because the user turned off music in the
 *   preferences.
 *   
 *   See the general notes on preferences queries above.  
 */
#define SYSINFO_PREF_MUSIC   13

/*
 *   Get link display preference setting - 0 = links are not being displayed
 *   because the user set a preference item that suppresses all links (which
 *   doesn't actually hide them, but merely displays them and otherwise
 *   treats them as ordinary text).  1 = links are to be displayed normally.
 *   2 = links can be displayed temporarily by the user by pressing a key or
 *   some similar action, but aren't being displayed at all times.  
 *   
 *   See the general note on preferences queries above.  
 */
#define SYSINFO_PREF_LINKS   14

/* can the system play MPEG sounds of any kind? */
#define SYSINFO_MPEG         15

/* can the system play MPEG audio 2.0 layer I/II/III sounds? */
#define SYSINFO_MPEG1        16
#define SYSINFO_MPEG2        17
#define SYSINFO_MPEG3        18

/* 
 *   is the system *currently* in HTML mode?  os_get_sysinfo never gets
 *   this code, since the portable output layer keeps track of this 
 */
#define SYSINFO_HTML_MODE    19

/* 
 *   Does the system allow following external URL links of the various
 *   types?  These return true if the system is capable of following these
 *   types of hypertext links, false if not.  Note that, if the system is
 *   capable of following these links, these should return true regardless
 *   of any current mode settings; in particular, these should not be
 *   sensitive to the current HTML mode or the current link display mode,
 *   since the question is not whether a link now displayed can be
 *   followed by the user, but rather whether the system has the
 *   capability to follow these types of links at all.  
 */
#define SYSINFO_LINKS_HTTP   20
#define SYSINFO_LINKS_FTP    21
#define SYSINFO_LINKS_NEWS   22
#define SYSINFO_LINKS_MAILTO 23
#define SYSINFO_LINKS_TELNET 24

/* is PNG transparency supported? */
#define SYSINFO_PNG_TRANS    25

/* is PNG alpha blending supported? */
#define SYSINFO_PNG_ALPHA    26

/* is the Ogg Vorbis audio format supported? */
#define SYSINFO_OGG          27

/* can the system display MNG's? */
#define SYSINFO_MNG          28

/* can the system display MNG's with transparency? */
#define SYSINFO_MNG_TRANS    29

/* can the system display MNG's with alpha blending? */
#define SYSINFO_MNG_ALPHA    30

/* can we display highlighted text in its own appearance? */
#define SYSINFO_TEXT_HILITE  31

/* 
 *   Can we display text colors?  This returns a SYSINFO_TXC_xxx code
 *   indicating the level of color support.
 *   
 *   The os_xxx interfaces don't presently support anything beyond the ANSI
 *   colors; however, HTML-enabled interpreters generally support full RGB
 *   colors, so we call this out as a separate level.  
 */
#define SYSINFO_TEXT_COLORS  32

/* no text color support */
#define SYSINFO_TXC_NONE      0

/* parameterized color names only (OS_COLOR_P_TEXT, etc) */
#define SYSINFO_TXC_PARAM     1

/* 
 *   we support only the basic ANSI colors, foreground control only (white,
 *   black, blue, red, green, yellow, cyan, magenta) 
 */
#define SYSINFO_TXC_ANSI_FG   2

/* ANSI colors, foreground and background */
#define SYSINFO_TXC_ANSI_FGBG 3

/* full RGB support */
#define SYSINFO_TXC_RGB       4

/* are the os_banner_xxx() interfaces supported? */
#define SYSINFO_BANNERS      33

/* Interpreter Class - this returns one of the SYSINFO_ICLASS_xxx codes */
#define SYSINFO_INTERP_CLASS 34

/* 
 *   Interpreter class: Character-mode Text-Only.  Interpreters of this class
 *   use a single, fixed-pitch font to display all text, and use the
 *   text-only HTML subset, and cannot display graphics.
 */
#define SYSINFO_ICLASS_TEXT    1

/* 
 *   Interpreter class: Text-Only GUI.  Interpreters of this class are
 *   traditional text-only interpreters running on graphical operating
 *   systems.  These interpreters might use multiple fonts (for example, they
 *   might display highlighted text in boldface), and might use
 *   proportionally-spaced text for some windows.  These interpreters support
 *   the text-only HTML subset, and cannot display graphics.
 *   
 *   Text-only GUI interpreters act essentially the same as character-mode
 *   text-only interpreters, from the perspective of the client program.  
 */
#define SYSINFO_ICLASS_TEXTGUI 2

/*
 *   Interpreter class: HTML.  Interpreters of this class can display
 *   graphics and sounds, can display multiple fonts and font sizes, can use
 *   proportional fonts, and support the full HTML TADS markup language for
 *   formatting.  
 */
#define SYSINFO_ICLASS_HTML    3

/*
 *   Audio fade information.
 *   
 *   SYSINFO_AUDIO_FADE: basic fade-in and fade-out support.  Interpreters
 *   that don't support audio fade at all should return 0.  Interpreters that
 *   support fades should return a bitwise OR'd combination of
 *   SYSINFO_AUDIOFADE_xxx flags below indicating which formats can be used
 *   with fades.
 *   
 *   SYSINFO_AUDIO_CROSSFADE: cross-fades are supported (i.e., simultaneous
 *   play of overlapping tracks, one fading out while the other fades in).
 *   If cross-fades aren't supported, return 0.  If they're supported, return
 *   a combination of SYSINFO_AUDIOFADE_xxx flags indicating which formats
 *   can be used with cross-fades.  
 */
#define SYSINFO_AUDIO_FADE       35
#define SYSINFO_AUDIO_CROSSFADE  36

/* 
 *   Specific audio fading features.  These are bit flags that can be
 *   combined to indicate the fading capabilities of the interpreter.  
 */
#define SYSINFO_AUDIOFADE_MPEG  0x0001          /* supported for MPEG audio */
#define SYSINFO_AUDIOFADE_OGG   0x0002          /* supported for Ogg Vorbis */
#define SYSINFO_AUDIOFADE_WAV   0x0004                 /* supported for WAV */
#define SYSINFO_AUDIOFADE_MIDI  0x0008                /* supported for MIDI */


/* ------------------------------------------------------------------------ */
/*
 *   Integer division operators.  For any compiler that follows ANSI C
 *   rules, no definitions are required for these routine, since the
 *   standard definitions below will work properly.  However, if your
 *   compiler does not follow ANSI standards with respect to integer
 *   division of negative numbers, you must provide implementations of
 *   these routines that produce the correct results.
 *   
 *   Division must "truncate towards zero," which means that any
 *   fractional part is dropped from the result.  If the result is
 *   positive, the result must be the largest integer less than the
 *   algebraic result: 11/3 yields 3.  If the result is negative, the
 *   result must be the smallest integer less than the result: (-11)/3
 *   yields -3.
 *   
 *   The remainder must obey the relationship (a/b)*b + a%b == a, for any
 *   integers a and b (b != 0).
 *   
 *   If your compiler does not obey the ANSI rules for the division
 *   operators, make the following changes in your osxxx.h file
 *   
 *   - define the symbol OS_NON_ANSI_DIVIDE in the osxxx.h file
 *   
 *   - either define your own macros for os_divide_long() and
 *   os_remainder_long(), or put actual prototypes for these functions
 *   into your osxxx.h file and write appropriate implementations of these
 *   functions in one of your osxxx.c or .cpp files.
 */
/* long os_divide_long(long a, long b);    // returns (a/b) with ANSI rules */
/* long os_remainder_long(long a, long b); // returns (a%b) with ANSI rules */

/* standard definitions for any ANSI compiler */
#ifndef OS_NON_ANSI_DIVIDE
#define os_divide_long(a, b)     ((a) / (b))
#define os_remainder_long(a, b)  ((a) % (b))
#endif

/* ------------------------------------------------------------------------ */
/*
 *   Special "switch" statement optimization flags.  These definitions are
 *   OPTIONAL - if a platform doesn't provide these definitions, suitable
 *   code that's fully portable will be used.
 *   
 *   On some compilers, the performance of a "switch" statement can be
 *   improved by fully populating the switch with all possible "case"
 *   values.  When the compiler can safely assume that every possible "case"
 *   value is specifically called out in the switch, the compiler can
 *   generate somewhat faster code by omitting any range check for the
 *   controlling expression of the switch: a range check is unnecessary
 *   because the compiler knows that the value can never be outside the
 *   "case" table.
 *   
 *   This type of optimization doesn't apply to all compilers.  When a given
 *   platform's compiler can be coerced to produce faster "switch"
 *   statements, though, there might be some benefit in defining these
 *   symbols according to local platform rules.
 *   
 *   First, #define OS_FILL_OUT_CASE_TABLES if you want this type of switch
 *   optimization at all.  This symbol is merely a flag, so it doesn't need
 *   a value - #defining it is enough to activate the special code.  If you
 *   don't define this symbol, then the code that cares about this will
 *   simply generate ordinary switches, leaving holes in the case table and
 *   using "default:" to cover them.  If the platform's osxxx.h header does
 *   #define OS_FILL_OUT_CASE_TABLES, then the portable code will know to
 *   fill out case tables with all possible alternatives where it's possible
 *   and potentially beneficial to do so.
 *   
 *   Second, if you #define OS_FILL_OUT_CASE_TABLES, you MUST ALSO #define
 *   OS_IMPOSSIBLE_DEFAULT_CASE.  The value for this symbol must be some
 *   code to insert into a "switch" statement at the point where a
 *   "default:" case would normally go.  On some compilers, special
 *   extensions allow the program to explicitly indicate within a switch
 *   that all possible cases are covered, so that the compiler doesn't have
 *   to be relied upon to infer this for itself (which would be impossible
 *   for it to do in many cases anyway).  You can use an empty definition
 *   for this symbol if your compiler doesn't have any special construct for
 *   indicating a fully-populated case table.
 *   
 *   Note that *most* switch statements in portable code won't care one way
 *   or the other about these definitions.  There's a time/space trade-off
 *   in fully populating a switch's case table, so only the most
 *   time-critical code will bother trying.  
 */



/* ------------------------------------------------------------------------ */
/*
 *   TADS 2 swapping configuration.  Define OS_DEFAULT_SWAP_ENABLED to 0
 *   if you want swapping turned off, 1 if you want it turned on.  If we
 *   haven't defined a default swapping mode yet, turn swapping on by
 *   default.  
 */
#ifndef OS_DEFAULT_SWAP_ENABLED
# define OS_DEFAULT_SWAP_ENABLED   1
#endif

/*
 *   If the system "long description" (for the banner) isn't defined, make
 *   it the same as the platform ID string.  
 */
#ifndef OS_SYSTEM_LDESC
# define OS_SYSTEM_LDESC  OS_SYSTEM_NAME
#endif

/*
 *   TADS 2 Usage Lines
 *   
 *   If the "usage" lines (i.e., the descriptive lines of text describing
 *   how to run the various programs) haven't been otherwise defined,
 *   define defaults for them here.  Some platforms use names other than
 *   tc, tr, and tdb for the tools (for example, on Unix they're usually
 *   tadsc, tadsr, and tadsdb), so the usage lines should be adjusted
 *   accordingly; simply define them earlier than this point (in this file
 *   or in one of the files included by this file, such as osunixt.h) for
 *   non-default text.  
 */
#ifndef OS_TC_USAGE
# define OS_TC_USAGE  "usage: tc [options] file"
#endif
#ifndef OS_TR_USAGE
# define OS_TR_USAGE  "usage: tr [options] file"
#endif
#ifndef OS_TDB_USAGE
# define OS_TDB_USAGE  "usage: tdb [options] file"
#endif

/*
 *   Ports can define a special TDB startup message, which is displayed
 *   after the copyright/version banner.  If it's not defined at this
 *   point, define it to an empty string.  
 */
#ifndef OS_TDB_STARTUP_MSG
# define OS_TDB_STARTUP_MSG ""
#endif

/*
 *   If a system patch sub-level isn't defined, define it here as zero.
 *   The patch sub-level is used on some systems where a number of ports
 *   are derived from a base port (for example, a large number of ports
 *   are based on the generic Unix port).  For platforms like the Mac,
 *   where the porting work applies only to that one platform, this
 *   sub-level isn't meaningful.
 */
#ifndef OS_SYSTEM_PATCHSUBLVL
# define OS_SYSTEM_PATCHSUBLVL  "0"
#endif

} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

#endif