summaryrefslogtreecommitdiff
path: root/web/funnelweb/examples/except.fw
blob: cb83fa0539947b046d637cf47a0052d9aa4956fd (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
@!******************************************************************************
@!*                                  except.fw                                 *
@!******************************************************************************

@t vskip 50 mm
@t title titlefont centre "Exceptions"
@t title titlefont centre "Package"
@t vskip 10 mm
@t title smalltitlefont centre "by Ross Williams"
@t title smalltitlefont centre "April 1993"
@t new_page
@t vskip 20 mm
@t title titlefont centre "Contents"
@t vskip 10 mm
@t table_of_contents
@t new_page

@!******************************************************************************
@!******************************************************************************

@A@<Overview@>

This overview section provides general information about this package.

Processing this file using FunnelWeb will result in the production of the
specification and implementation files defined below. In addition a test
program for testing this package will be generated. This FunnelWeb source
file and the files that it generates are Copyright (C) Ross Williams 1993.
See the configuration header notice below.

@O@<except.h@>@{@<Specification file@>@+@}
@O@<except.c@>@{@<Implementation file@>@+@}
@O@<ex_test.c@>@{@<Test program file@>@+@}

The remainder of this overview section contains only descriptions. It does
not contain any code.

@!******************************************************************************

@B@<Configuration Header Notice@>

The following notice applies to this FunnelWeb source file as well as to
the FunnelWeb output files to which it is written.

@$@<Configuration header notice@>@M@{
/*************************************************************************/
/* Package name  : Except.                                               */
/* Version       : 1.0                                                   */
/* Completed     : April 1993.                                           */
/* Released      : 29 September 1993.                                    */
/* First created : This package was first created in April 1993.         */
/* Summary       : This package provides Ada-like exceptions for C.      */
/* Components    : except.fw  - FunnelWeb source file.                   */
/*                 except.h   - Exported header file.                    */
/*                 except.c   - Implementation file.                     */
/*                 ex_test.c  - C test program.                          */
/* Requires      : style.h, as.h, as.c.                                  */
/* Author        : Ross N. Williams (ross@@guest.adelaide.edu.au)         */
/*                 Rocksoft^tm Pty Ltd                                   */
/*                 16 Lerwick Avenue, Hazelwood Park 5066, Australia.    */
/* FTP Archive   : This file can be found in                             */
/*                 "ftp.adelaide.edu.au/pub/funnelweb/examples/"         */
/* Disclaimer    : This program is distributed WITHOUT ANY WARRANTY;     */
/*                 without even the implied warranty of MERCHANTABILITY  */
/*                 or FITNESS FOR A PARTICULAR PURPOSE.                  */
/* Copyright     : Copyright (C) Ross Williams 1993.                     */
/*                 However, permission is granted for anyone to copy,    */
/*                 modify, and distribute this work for any purpose,     */
/*                 commercial or non-commercial, so long as this notice  */
/*                 is included verbatim, and so long as all              */
/*                 modifications are recorded in the change log below.   */
/* Changes       : Please log any changes to this software either in the */
/*                 originating FunnelWeb source file, or, if you must,   */
/*                 in the C source files produced from the FunnelWeb     */
/*                 file.                                                 */
/* --<Start of Change Log>--                                             */
/* ??-Apr-93: RNW: Created this package.                                 */
/* 29-Sep-93: RNW: Released this package.                                */
/* --<End of Change Log>--                                               */
/*************************************************************************/
@}

@!******************************************************************************

@B@<Abstract@>

This package provides an @/exceptions@/ facility for C that is very similar to
the exceptions facility in the Ada programming language. An exception is a
control-flow event that causes control to be transferred up the stack
(with the stack unwinding as it goes) until a @/handler@/ for the particular
exception invoked is found. The exception facility is totally under the
client's control. The client defines a set of exceptions, and @/raises@/ an
exception event by calling @{EX_RAISE@}. Other functions defined by the client
can catch the exceptions by name. The exception facility is useful for
error handling and package interface definition.

@!******************************************************************************

@B@<Motivation@>

The motivation for creating this package was to provide a simpler, safer, and
more sophisticated non-local jump facility than is provided by the standard
ANSI @{setjmp@} (ANSI 7.6.1.1) and @{longjmp@} (7.6.2.1) primitives. In
particular, the biggest problem with the ANSI functions is that they
require that the thrower and catcher of the control event be sufficiently
coordinated to share a single global variable containing the catcher's
context. The requirement for this link is unacceptable because it precludes
the use of non-local jumps as an element of the interface of packages
having an abstract client (unless @{jmpbuf@}s are passed as parameters - yuk!).
This lack of abstraction results in the following disadvantages:

* A global variable of type @{jmpbuf@} must be agreed upon between thrower
and catcher for each potential throw. The result is a proliferation of
global context variables, whose validity at any particular instant is often
unclear.

* If the programmer wishes to set up a nest of handlers for a particular
condition, the global @{jmpbuf@} buffer for the condition must be explicitly
and carefully saved and restored by each function in the nest.

* The interface to @{setjmp@} and @{longjmp@} is a bit raw, requiring
fiddling with integer values and embedding calls in conditional statements.

A better solution to the problem of non-local jumps is a system of
exceptions.

@!******************************************************************************

@B@<What is an Exception?@>

An exception is essentially a non-local @{goto@} to the nearest dynamically
enclosing target label in the stack of suspended functions. To execute an
exception @{goto@}, one @/raises@/ the exception, for example with a @{raise@}
statement @{raise sloth_exception;@}. Control is then immediately transferred
to the nearest dynamically enclosing handler (label) for @{sloth_exception@}.

In fact handlers are not simply labels, but block-structured constructs
that define an execution zone within which the set of exceptions that they
handle will be caught. For example, the following exception handling
anonymous block might appear in a program written in the Ada programming
language:

@$@<Example of an Ada exception handling block@>@Z@{
declare
   n : integer := 2*x+y;
begin
   sloth_action(n);
   walrus_action(n*n);
exception
   when sloth_exception    => PUT("The sloth    exception went off.");
   when aardvark_exception => PUT("The aardvark exception went off.");
end
@}

In the above example, the code between @{declare@} and @{exception@} is
the code within which exceptions will be caught. The code between
@{exception@} and @{end@} contains two exception handlers that will catch
the exceptions @{sloth_exception@} and @{aardvark_exception@} should they
be raised by the execution of the declarations or @{sloth_action@} or
@{walrus_action@}. Any other exception that is raised within the controlled
zone will simply propagate further up the call chain to the nearest handler
that can handle it.

A most important aspect of the exceptions provided by Ada and this package
is that an exception propagates to the nearest @/dynamically@/ enclosing
exception handler. This means that, apart from the shared knowledge of the
definition of the exception, there is not necessarily any static relationship
between the function that raises an exception and the function that catches
it. This makes exceptions both dangerous and powerful. Dangerous because a
programmer can code a @{raise@} statement with no knowledge of the function
catching the exception. Powerful, because this very abstraction allows
packages to raise exceptions that can be caught by abstract clients.

@!******************************************************************************

@B@<Exceptions as Active Statuses@>

Over the years, exceptions have received a lot of bad press and have
generally been considered to be dangerous things. For example, Tony Hoare,
in his Turing Award lecture of 1980, when covering the programming
language Ada, remarked:

@/Gradually these objectives have been sacrificed in favour of power,
supposedly achieved by a plethora of features and notational conventions,
many of them unncessary and some of them, like exception handling,
even dangerous.@/ --- Charles Antony Richard Hoare, Turing Award Speech, 1980.

Certainly exceptions can be dangerous things. However, I believe that, on
balance, exceptions enhance reliability rather than deteriorating it. The
reason for this belief is that exceptions @/actively@/ indicate problems,
whereas their alternative in this capacity, statuses, @/passively@/ indicate
problems.

To see this, consider the simple example case of a function that is supposed
to write its argument character to its argument device. This is a
straightforward operation, unless the device fails in some way, in which case
this fact must be indicated to the caller. The traditional way of defining such
a function is to have it return a status indicating whether the call succeeded.

@$@<Example of a function returning a status@>@Z@{
EXPORT bool writdev P_((dev_t,char));
/* Returns TRUE upon success, FALSE if the write failed. */
@}

This results in calling code that should look something like this:

@$@<Example of call to function returning a status@>@Z@{
if (!writdev(slothdev,'x'))
  bomb("Sloth device failed - aborting.\n");
@}

but often looks like this:

@$@<Example of call without checking to a function returning a status@>@Z@{
writdev(slothdev,'x');
@}

Code such as this is extremely dangerous because a program written in this
way might operate correctly for years, but then suddenly start behaving in
odd ways when undetected errors occur. Unfortunately, it
is extremely easy to write code such as this, as is easy to forget that
a particular function even returns a status! Code such as this is quite
common.

Now consider how @{writdev@} looks if it is defined using an exception to
take care of the error case:

@$@<Example of a function that uses an exception@>@Z@{
EXPORT void writdev P_((dev_t,char));
/* Raises writedev_ex exception if the write fails. */
@}

By casting the error condition in terms of an exception, the function
@/by default@/ ensures that something will happen if an error arises.
In fact, one of two things must happen if the exception is ever raised: either
the programmer codes a catcher for the exception and deals with it (in which
case, it will probably be dealt with properly) or the exception propagates
up out of @{main()@} where it will cause the exceptions package to
initiate a controlled program bomb describing the unhandled exception.
Both of these alternatives ensure that the condition is explicitly handled
which is the important thing.

By using exceptions, the programmer can ensure that @/by default@/ any
error condition that arises will cause a program bomb. This is such a
strong safety net that it actually @/legitimizes@/ the no-error checking
style of programming so criticized earlier. If you are in a hurry to write
a program and do not wish to bother with error checking, the use of
functions that raise exceptions upon errors ensures that if the code
terminates normally, then it has terminated error-free --- even though
the programmer did not code any checking of return conditions.

Another advantage of exceptions is that they allow whole classes of errors
that might arise in a span of code to be handled at one point. For example:

@$@<Example of single-point error handling@>@Z@{
EX_BEGIN
   f_file_t file1, file2;
   char line[1000];
   f_open(file1,read,"sloth.dat");
   f_open(file2,write,"walrus.dat");
   while (!f_eof(file1))
     {
      f_readline (file1,line,1000);
      f_writeline(file2,line,1000);
     }
   fclose(file1);
   fclose(file2);
EX_FORGET
   EX_WHEN(f_open_ex ) goto f_handle;
   EX_WHEN(f_read_ex ) goto f_handle;
   EX_WHEN(f_write_ex) goto f_handle;
   EX_WHEN(f_close_ex)
   f_handle:
      f_delete(file2);
      printf("Error performing copy.");
EX_END@}

In the above code, a single exception handler handles all the error conditions
that might arise during all the file operations. The resultant code is far
cleaner than the code that would be necessary if the status of each call
had to be checked independently. Using exceptions, the normal and the
exceptional cases can each be expressed clearly.

@/Summary:@/ There is no doubt that, when misused, exceptions can be
an extremely dangerous language construct. However, they also have the
outstanding quality of enabling functions to be written that @/actively@/
indicate errors, thus ensuring either that the client code does @/something@/
about the error, or that the program will bomb with an unhandled exception
error. In this capacity exceptions act as @/active statuses@/. In addition,
exceptions allow error handling to be separated from ordinary execution.
These two properties of exceptions mean that, in my opinion, they improve
reliability on balance, and are a worthy programming tool.

@!******************************************************************************

@B@<How to Use This Package@>

Using this package is easy - just follow the following steps!

@/STEP 1: Define the exception.@/ Before raising or catching an exception,
you have to define it and make it visible to every entity that needs to
refer to the exception. To define it, you need to think of an identifier that
will be used to represent the exception, and you need to think of a short
(one-line) description for the exception. Having done this, if you want
the exception to be visible only within the current program file, insert
a call to the macro @{EX_LOCAL@} in the variable section. For example:

@$@<Example exception definition@>@Z@{
EX_LOCAL(buf_full,"buf_full: Buffer is full exception.");
@}

The first argument is the @/exception identifier@/ and the second argument is
the @/exception description@/. The @{EX_LOCAL@} macro translates into a
string constant declaration having the name @{buf_full@} and the value
@{"buf_full: Buff..."@}. It is important that the exception identifer be
unique within its scope as it is implemented by an ordinary constant. It
is also very important that the description of the exception be unique
within the set of exceptions in the program. If each exception has a unique
description, then unhandled exceptions (see later) can be easily identified.

If you want the exception to be visible within all clients of the module
declaring it, replace @{EX_LOCAL@} above with @{EX_EXPORT@} and insert a
call to @{EX_EXCEPT@} in the header file for the module. @{EX_EXCEPT@}
requires just the variable name.

@$@<Example exception declaration@>@Z@{
EX_EXCEPT(buf_full);
@}

@/STEP 2: Raise the exception.@/ Having completed step 1, you can now raise the
exception in any module that can see the exception (i.e. the @{.c@} file
with the @{EX_LOCAL@} or @{EX_EXPORT@}, and any @{.c@} file that includes
the header file containing the @{EX_EXCEPT@}) simply be calling @{EX_RAISE@}.
Note: @{EX_RAISE@} is syntactically equivalent to a compound statement.

@$@<Example of raising an exception@>@Z@{
/* If the buffer is full, raise the full buffer exception. */
if (bufsiz > BUF_MAX)
   EX_RAISE(buf_ful);
@}

The effect of a call to @{EX_RAISE@} is that control will leave the current
function, and the stack will unwind until a function is encountered whose
context contains an active exception handler construct that specifies a
catcher for the exception that has been raised (in this case @{buf_ful@}.
If no such handler exists, the exception package will bomb the program,
printing out an error message stating that an exception has not been
handled, and giving the description of the exception.

@/STEP 3: Catch the exception.@/ Catching exceptions is a little tricker than
raising them. To catch an exception, you need to specify so in advance
by enclosing the code that might raise the exception within an @/exception
block@/ construct provided by the @{EX_@} macros @{EX_BEGIN@}, @{EX_FORGET@},
@{EX_WHEN@} and @{EX_END@}. Here is an abstract example:

@$@<Exception block syntax@>@Z@{
EX_BEGIN
   <Code that might raise an exception>
EX_FORGET
   EX_WHEN(<name of exception 1>)
      <Code to handle exception 1>
   EX_WHEN(<name of exception 2>)
      <Code to handle exception 2>
   EX_WHEN(<name of exception 3>)
      <Code to handle exception 3>
EX_END
@}

The entire context (from @{EX_BEGIN@} to @{EX_END@}) is syntactically
equivalent to a compound statement (@{{}@}). Its semantics are that the
code that might raise an exception is executed, and, if no exceptions are
raised, terminates. If an exception is raised, the code aborts to
the corresponding handler, or is propagated upwards if no matching handler
is present. For a more detailed description of the semantics, see the
specification section.

@!******************************************************************************
@!******************************************************************************

@A@<Static Parameters of this Package@>

This package has a few static (compile-time) parameters which can be used
to modify its behavior.

@!******************************************************************************

@B@<Inline Switch Parameter@>

Exceptions are tricky constructs, particularly in a programming language
like C, and it makes sense to incorporate lots of checking into a package
such as this one. At the same time, exceptions are a fundamental language
construct that are likely to be used extensively, and it should be possible
to configure them to be as efficient as possible.

To satisfy these twin needs, this package provides a boolean parameter which
can be used to select two different kinds of implementation. Set @{_EX_FAST@}
to @{FALSE@} if you want the exported macros to generate function calls and to
perform lots of checks. Set @{_EX_FAST@} to @{TRUE@} if you want the exported
macros to generate inline code that runs as fast as possible with no checking
at all. It is strongly recommended that this symbol be set to @{FALSE@}
unless performance requirements absolutely demand that it be set to @{TRUE@}.

@$@<Symbol _EX_FAST@>@{
#define _EX_FAST FALSE
/* Set _EX_FAST to TRUE to turn on inlining and turn off checking. */
@}

Note: Some other packages (such as the @{link@} package) allow parameters
like this to be set by the client package, thus enabling different client
modules to specify different safety/efficiency trade-offs. However, this level
of parameterization granularity could not be achieved in this package as
some of the checking is magic number checking which must be employed globally
or not at all. The problem is that a module with checks on might complain
about incorrect magic numbers that were not set by another module with checks
off.

@!******************************************************************************

@B@<Threading Parameter@>

The parameter @{_EX_THRD@} tells the package whether it is operating within
a multi-threaded environment (@{TRUE@}) (thus prohibiting single-instance
global variables). If you set this to @{TRUE@}, you will also need to
modify the macros for global variables in the next section.

@$@<Symbol _EX_THRD@>@{
#define _EX_THRD FALSE
/* To configure this package to work under a multi-threaded environment, */
/* set _EX_THRD to TRUE and modify the k_var calls later on in the file. */
@}

@!******************************************************************************

@B@<Macros for Global Variables@>

This package uses three global variables --- @{_ex_curr@}, @{_ex_id@},
and @{_ex_info@} --- which root the exception context stack, and store
information about the most recently raised exception. Unfortunately,
global variables such as these cause problems on multi-threaded
systems which require that separate instances of all global variables be
maintained for each thread.

The solution to this problem is to define the global variables as usual,
but to refer to them only through a set of lvalue-yielding macros. These
macros can then be configured to point either directly to a single set of
global variables (non-threaded system), or to thread-instance variables
(threaded system). This is the approach that has been taken in this package.
Accordingly the macros have been defined in this package static
parameter section. Both unthreaded and threaded versions appear below.

@$@<Macros for global variables@>@{
#if !_EX_THRD
/* Non-threaded system macros simply point to variables. */
#define _EX_CURR _ex_curr
#define  EX_ID   ((p_ex_t) _ex_id)
#define _EX_ID   _ex_id
#define  EX_INFO _ex_info
#else
/* Threaded system macros translate to kernel calls.          */
/* To make this package work in a multi-threaded environment, */
/* set _EX_THRD to TRUE and modify these definitions.         */
/* Note: The kernel should initialize _EX_CURR to NULL.       */
#define _EX_CURR           (* ((_ex_cx_t *) k_var(51)))
#define  EX_ID   ((p_ex_t) (* ((p_ex_t   *) k_var(52))))
#define _EX_ID             (* ((p_ex_t   *) k_var(52)))
#define  EX_INFO           (* ((ptrint   *) k_var(53)))
#endif
@}

(The 51, 52 and so on are just example variable numbers. By the way, each of
the three global variables will fit into a @{ptrint@}.).

The global variable @{_ex_curr@} is not supposed to be used by the client
so its symbol starts with @{_@}. @{EX_ID@} and @{EX_INFO@} are
intended to be visible to the client. However, the client is not allowed
to write to @{EX_ID@} and so two forms have been provided, one hidden
read/write form (@{_EX_ID@}) and one read-only form (@{EX_ID@}). The type cast
in @{EX_ID@} is there solely to convert the value from an lvalue to an rvalue
(ANSI 6.3.4 (footnote 44)) so that the client can't write to it.

If these macros are changed to point to thread instance variables, be
sure to initialize the @{_EX_CURR@} variable to @{NULL@} (before performing
any exception actions) if you want unhandled exceptions to be correctly
detected. The other two variables don't need to be initialized.

Because these macros can translate to function calls, an
attempt has been made in the rest of the package to minimize references to
them (e.g. assign their values to temporary variables and manipulate them
rather than using the macro itself multiple times).

@!******************************************************************************
@!******************************************************************************

@A@<Specification@>

Because this package exports lots of macros, its specification (@{.h@}) file
contains lots of stuff that should really be in the implementation file out
of sight of the client.  Luckily, FunnelWeb allows most of these implementation
details to be located in the implementation section of this FunnelWeb file,
leaving this section to describe, as cleanly as possible, only those components
in the specification file intended to be presented to the client.

In a nutshell, the package exports an exception type, and macros to raise
and catch exceptions.

@$@<Specification file@>@{
@<Configuration header notice@>
#ifndef _EX_DONE
#define _EX_DONE 1
@<Specification includes@>
@<Exception type@>
@<Hidden definitions@>
@<Macro EX_RAISE@>
@<Macros for constructing exception blocks@>
@<Function spec ex_str@>
@<Hidden function specs@>
#endif
@}

@!******************************************************************************

@B@<Specification Includes@>

The specification file requires just the following includes. The @{style.h@}
include file contains basic C style stuff. The ANSI standard @{setjmp.h@}
include file (ANSI Standard Section 7.6) is required because the @{setjmp@}
and @{longjmp@} functions/macros are used to implement the package.

@$@<Specification includes@>@{
#include "style.h"
#include <setjmp.h>
@}

@!******************************************************************************

@B@<Exception Type@>

Under this package, each exception is represented by an explicit object that
resides in memory. However, the package does not export this object type.
Like many C packages that export abstract types, this package exports only
a pointer-to-object type, the intent being that the client should only ever
manipulate the abstract objects through pointers. Thus, this package exports
a pointer-to-exception type @{p_ex_t@}, but does not export a corresponding
exception type @{ex_t@}.

Most packages that export abstract objects by pointer type also export
functions to dynamically create and destroy the abstract objects. However,
in the case of exceptions there is no need for this. In fact there is a
positive need to create exceptions at compile time and bind them statically
(Dynamic creation would mean that an exception would have to be created before
being raised, which would be awkward to coordinate at run time).
So, instead of exporting a function to create an exception, this package
exports macros that allow the client to statically create (i.e. at compile
time) a new (hidden) exception object and, simultaneously, define a constant
pointer (of type @{p_ex_t@}) that points to the new exception object, and
to which an identifier of the client programmer's choosing can be bound.
Here are the macros that do this.

@$@<Exception type@>@{
@<Type p_ex_t@>
#define EX_EXCEPT(NAME)      @<Implementation of EX_EXCEPT@>
#define EX_EXPORT(NAME,DESC) @<Implementation of EX_EXPORT@>
#define EX_LOCAL(NAME,DESC)  @<Implementation of EX_LOCAL@>
@}

The macros @{EX_EXPORT@} and @{EX_LOCAL@} each create a brand new exception
object. They can be called only at places in a module where variables can be
declared. The argument @{DESC@} must be a one-line constant string that
describes the exception; it will be written out if the exception is involved
in some kind of error (e.g. if it propagates out of @{main()@} and has to be
described to the user). The @{NAME@} argument must be a program identifier
which will be the name of the pointer constant that will point to the
new exception. Macros @{EX_EXPORT@} and @{EX_LOCAL@} are identical except
that @{EX_EXPORT@} makes the constant pointer globally visible in the
program (i.e. it is present in the symbol table at link time), whereas
@{EX_LOCAL@} does not. If @{EX_EXPORT@} has been used, the constant pointer
to the exception can be made visible in other modules by adding an instance
of the @{EX_EXCEPT@} macro in the header file of the module containing the
exception definition.

Here is an example of a global exception declaration:

@$@<Example exception declarations@>@Z@{
In sloth.h:
   EX_EXCEPT(sloth_exception);

In sloth.c:
   EX_EXPORT(sloth_exception,"Sloth exception");
@}

@!******************************************************************************

@B@<Macro EX_RAISE@>

The macro @{EX_RAISE@} raises its argument exception. The argument exception
must be an expression evaluating to a pointer to the exception to be raised.
See a later section for the precise semantics of an exception raise.

This macro is syntactically equivalent to a compound statement. It is
guaranteed that its argument will be evaluated exactly once.

@$@<Macro EX_RAISE@>@{
#if _EX_FAST
#define EX_RAISE(P_EX) @<Fast implementation of EX_RAISE(P_EX)@>
#else
#define EX_RAISE(P_EX) @<Safe implementation of EX_RAISE(P_EX)@>
#endif
@}

@!******************************************************************************

@B@<Exception Block Construct@>

The only way to catch one or more exceptions is to declare a construct
that encloses the code that could raise the exceptions, along with the handler
code. This construct is called an exception block and is implemented using
a small set of preprocessor macros. Here they are:

@$@<Macros for constructing exception blocks@>@{
#if _EX_FAST
#define EX_BEGIN    \@+@<Fast implementation of EX_BEGIN@>
#define EX_FORGET      @<Fast implementation of EX_FORGET@>
#define EX_WHEN(P_EX)  @<Fast implementation of EX_WHEN(P_EX)@>
#define EX_OTHERS      @<Fast implementation of EX_OTHERS@>
#define EX_END         @<Fast implementation of EX_END@>
#define EX_POP         @<Fast implementation of EX_POP@>
#else
#define EX_BEGIN    \@+@<Safe implementation of EX_BEGIN@>
#define EX_FORGET      @<Safe implementation of EX_FORGET@>
#define EX_WHEN(P_EX)  @<Safe implementation of EX_WHEN(P_EX)@>
#define EX_OTHERS      @<Safe implementation of EX_OTHERS@>
#define EX_END         @<Safe implementation of EX_END@>
#define EX_POP         @<Safe implementation of EX_POP@>
#endif
@}

These macros must be used in accordance with the following syntax:

@$@<Syntax of exception block@>@Z@{
compound_statement = other_forms_of_compound_statement | exception_block
                   | EX_POP
exception_block = "EX_BEGIN" c_code "EX_FORGET" { handler } "EX_END"
handler         = "EX_WHEN(" expression ")" c_code
handler         = "EX_OTHERS"
@}

The argument to each instantiation of @{EX_WHEN@} must be an rvalue of type
@{p_ex_t@}. It is guaranteed that this argument will be evaluated @/at most@/
once.

The macro @{EX_POP@} should be invoked only from within the normal code of
an exception block.

@!******************************************************************************

@B@<Definition of Semantics@>

This section contains a detailed English description of the semantics of the
exception constructs provided by this package. Normally this sort of
information would be laced throughout the package specification. However, in
this package, there are so many special cases that it was thought best to
collect all the tricky stuff in one place. The reader is advised to study
this section thoroughly, as a lack of knowledge about the precise semantics
of the exception construct can be very dangerous.

@/Basic semantics:@/ When control hits an exception block,
the code between @{EX_BEGIN@} and @{EX_FORGET@} (the
@/normal code@/) is executed. If, during the execution of the normal code, no
exceptions propagate up to this construct then, upon completion of the normal
code, execution of the entire construct terminates. If, during the execution
of the normal code, an exception is raised (in the code or any function it
calls) that propagates up to this construct, control exits the normal code at
that point and is transferred to the code associated with the handler for
the exception raised (the @/handler code@/). If there is no handler for the
exception raised, the exception is propagated to the next dynamically
enclosing handler in the dynamic chain. If there is a handler for the target
exception, the code associated with that handler is executed and the
construct terminates. If an exception propagates out of @{main()@}, the
exception package bombs the program with an error message explaining what
has happened and printing out the description of the offending exception.

@/State of the function:@/ You may be wondering why the middle keyword
in the exception construct is @{EX_FORGET@}. The reason is that the ANSI
standard (ANSI 7.6.2.1) states: @/All accessible objects have values as of
the time longjmp was called, except that the values of objects of automatic
storage duration that are local to the function containing the invocation
of the corresponding setjmp macro that do not have volatile-qualified type
and have been changed between the setjmp invocation and longjmp call
are indeterminate.@/ These are strong words! Because this exception package
is implemented using @{setjmp@} and @{longjmp@}, these words mean that
following the raising of an exception, you cannot rely on the value of any
@{auto@}matic variable that was modified within the normal code of an
exception construct before the exception was raised. The @{EX_FORGET@} syntax
has been designed to act as a continual reminder of this danger.

@/Direct exits from normal code:@/ Because the exception package
maintains an explicit stack of exception handling contexts, there are some
restrictions about how control can leave such constructs. ``Acceptable'' ways
of leaving the main code of an exception block are 1) reaching the end of the
block, and 2) raising an exception (handled or unhandled). However, control
can also exit the construct in four other ``unacceptable'' ways. These are
1) by executing a @{goto@} out of the block,
2) by executing a @{return@},
3) by executing an explicit @{longjmp@},
4) by executing @{exit@} or some other program terminating function
(actually this case does not matter as much as the program ends anyway).
To ensure that the exception package correctly maintains its stack of
live exception blocks, each instance of any of the four unacceptable ways
of exiting a block should be prededed by a call to @{EX_POP@}. Here is
an example of a correct explicit exception block termination.

@$@<Example of a correct direct exit from an exception block@>@Z@{
EX_BEGIN
   add_char(buffer,'x');
   if (is_full(buffer)) {EX_POP; return TRUE;}
EX_FORGET
   EX_WHEN(buf_ful)
      return TRUE;
EX_END
return FALSE;
@}

@{EX_POP@} can only be called from within the normal code of an exception
block.

@/No restrictions on exiting handler code:@/ By the time control reaches any
exception handler code, the current exception context has been popped, so
there are no constraints at all on exits from handler code. In the example
above, the @{return TRUE@} from the normal code must be preceeded by a call
to @{EX_POP@}, but no such call is required for the @{return TRUE@} in the
handler code. In fact, such a call would be erroneous. As soon
as control hits handler code it is guaranteed that the current context
has been popped.

@/Exception handler code can jump to other exception handlers:@/ Just place a
label inside one handler and a goto in another. This is useful where the code
in one handler is suitable for many exceptions. For example:

@$@<Example of goto in exception handler@>@Z@{
EX_BEGIN
   add_char(buffer,'x');
EX_FORGET
   EX_WHEN(misc_1) goto doit;
   EX_WHEN(misc_2) goto doit;
   EX_WHEN(misc_3) goto doit;
   EX_WHEN(misc_4) goto doit;
   EX_WHEN(misc_5)
     doit:
        printf("Miscellaneous exception happened.\n");
        return FALSE;
EX_END
@}

Because there are no restrictions on how handlers can exit, the @{gotos@}
could have pointed outside the construct too.

@/Local variables:@/ Each normal code and handler code segment is actually
framed as a compound statement. This means that you can declare local
variables if desired. For example:

@$@<Example of local variables in an exception block@>@Z@{
EX_BEGIN
   int result;
   result=add_char(buffer,'x');
   if (result > 10) {EX_POP; return TRUE;}
EX_FORGET
   EX_WHEN(buf_ful)
      string walrus = "An example string.\n";
      printf(walrus);
      return TRUE;
EX_END
return FALSE;
@}

@/Exception blocks can be nested:@/ Functions can contain many exception
blocks. Exception blocks can even be nested. However, be very sure to obey
the other rules. Here is an example:

@$@<Example of nested exception handlers@>@Z@{
EX_BEGIN
   add_char(buffer,'x');
   EX_BEGIN
      big_func();
      EX_FORGET
         EX_WHEN(buf_ful) printf("big_func raised buf_ful.\n");
   EX_END
EX_FORGET
   EX_WHEN(buf_ful) printf("add_char raised buf_ful.\n");
   EX_WHEN(sloth)   printf("either add_char or big_func raised sloth.\n");
EX_END
@}

@/OTHERS handler:@/ The @{EX_OTHERS@} handler catches all exceptions.

@/Handler precedence:@/ The first handler that matches an exception
catches the exception. If more than one handler matches the same exception,
the first handler will be activated.

@/Re-raising an exception:@/ To re-raise an exception, simply
code the statement @{EX_RAISE(EX_ID);@}.

@!******************************************************************************

@B@<Exception Information@>

When an exception is raised, two symbols provide information about the
exception being raised. These symbols are macros and do not necessarily
translate to variable names. However, it is guaranteed that @{EX_ID@} will be
an rvalue (ANSI 6.2.2.1 (footnote 31)) of type @{p_ex_t@}, and that @{EX_INFO@}
will behave like an modifiable lvalue (ANSI 6.2.2.1) of type @{ptrint@} (see
the header file @{style.h@} for this type). The declarations of these macros
appear in the static parameters section of this document.

Whenever an exception is raised, @{EX_ID@} contains the ID of the exception
being raised. This value persists until the next exception is raised.
This variable is only marginally useful in the current version of
this package, as each exception handler will always statically ``know'' the
exception that it has caught. In long and complex handlers though, the
capacity for self-reference that @{EX_ID@} provides enables such handlers
to be coded without hard-wiring the exception in question whenever it is
re-raised. This allows exceptions to be more easily renamed. The macro is
also useful because it allows exceptions to be dynamically re-raised from
code to which many handlers have been pointed to with @{goto@}s. For example:

@$@<Example of use of EX_ID@>@Z@{
EX_BEGIN
   add_char(buffer,'x');
EX_FORGET
   EX_WHEN(misc_1) goto doit;
   EX_WHEN(misc_2) goto doit;
   EX_WHEN(misc_3) goto doit;
   EX_WHEN(misc_4) goto doit;
   EX_WHEN(misc_5)
     doit:
        printf("Miscellaneous exception happened.\n");
        EX_RAISE(EX_ID);
EX_END
@}

@{EX_ID@} has been configured as an rvalue so that it cannot be written.
Thus, it is guaranteed always to contain the ID of the exception most recently
raised.

The variable @{EX_INFO@} is more under client control. It exists simply to
act as an abstract channel for code raising exceptions to communicate with
code catching exceptions. There are no rules for this value --- it can be
written to, or read from any piece of code at any time; make up your own
rules. The type of @{EX_INFO@} has been set to @{ptrint@} so as to allow
any pointer or integer to be conveyed through the variable. This effectively
allows any amount of information to be passed.

@!******************************************************************************

@B@<Function ex_str@>

The function @{ex_str@} accepts a pointer to an exception and returns a
pointer to a constant static string being the one line description of the
exception that was provided when the exception was defined with @{EX_LOCAL@}
or @{EX_EXPORT@}.

@$@<Function spec ex_str@>@{EXPORT string ex_str P_((p_ex_t));@}

@!******************************************************************************

@B@<A Warning About Names@>

This package exports a number of compile-time and link-time symbols that
pollute the symbol table and link table. This pollution is necessary because
this package relies significantly on exported macros that reference exported
variables. To reduce naming conflicts, nearly all exported names that are
supposed to be visible to the client commence with @{ex_@} and all exported
names that are supposed to be invisible to the client commence with @{_ex@}.
Please be careful to avoid names with these prefixes.

@!******************************************************************************

@B@<Glossary@>

@{Abstract client@} --- A client of this package about which no information
is known.

@{Catch@} --- When an exception is raised, it propagates up the run-time
context stack until it encounters a corresponding handler at which point it
is said to have been caught.

@{Client@} --- Any code that uses this package.

@{Exception@} --- A dynamic non-local control-flow event.

@{Exception block@} --- A special kind of anonymous block (called a compound
statement in C) containing exception handlers.

@{Exception description@} --- A single-line string briefly describing the
exception. This string is printed out whenever an exception has to be
identified to the user.

@{Exception identifier@} --- A program identifier that is bound to a constant
whose value is a pointer pointing to an exception object.

@{Exception handler@} --- An exception/code binding that appears at the
end of an exception block and catches the specified exception should it
propagate to that level.

@{Direct exit@} --- The termination of an exception block in a way that
bypasses the exception package (for example, by @{return@} or explicit
@{longjmp@}). The macro @{EX_POP@} should be called before all such exits.

@{Raise@} --- An exception is raised by an @{EX_RAISE@} statement. Once
raised, an exception unwinds the stack until it encounters a handler for
the specific exception.

@!******************************************************************************
@!******************************************************************************

@A@<Implementation@>

This section describes the implementation of this package. The implementation
is short, but is rather messy because of the fast/safe alternatives, and
because of the fact that much of the functionality of the package is provided
in the @{.h@} file. Much of that functionality has been deferred using
FunnelWeb macros to this section.

The implementation (@{.c@}) file itself is fairly simply, consisting only
of some global variables and a few function definitions.

@$@<Implementation file@>@{
@<Configuration header notice@>
#include "except.h"
#include "as.h"
@<Global variables (.c)@>
@<Function ex_str@>
@<Function ex_bomb@>
@<Function _exrai@>
@<Function _expop@>
@}

All the real action happens in the definitions in the specification (@{.h@})
file that are hidden from the client.

@$@<Hidden definitions@>@{
@<Symbol _EX_FAST@>
@<Symbol _EX_THRD@>
@<Exception block context type@>
@<Global variables (.h)@>
@<Macros for global variables@>
@}

@$@<Hidden function specs@>@{
@<Function spec _exrai@>
@<Function spec _expop@>
@}

@!******************************************************************************

@B@<Overview and Data Structure Description@>

This section gives an overview of the implementation of this package.

This package operates by maintaining an explicit stack of currently active
exception blocks. Whenever an exception block is entered (@{EX_BEGIN@}),
a @{setjmp@} is executed and the resultant @{jmp_buf@} is inserted into a
stack node and pushed onto the stack. Whenever an exception block is exited
normally (@{EX_END@}), the stack node on the top of the stack is popped. When
an exception is raised, the top of the stack is popped off and a @{longjmp@}
is executed to its context. The context then searches its handlers for a
handler for the exception raised. If it finds one, it executes it. If it
doesn't, it pops and @{longjmp@}s the top of stack again. This goes on
until the exception is caught, or the stack is empty.

The stack is composed of a singly-linked list of records of the following
type:

@$@<Exception block context type@>@{
typedef struct _ex_cxt_
  {
@<Header magic number field@>
   jmp_buf          _ex_jmbf;  /* Exception block context.          */
   struct _ex_cxt_ *_ex_prev;  /* Pointer to previous stack record. */
@<Trailer magic number field@>
  } _ex_cx_t;
@<Magic number symbols@>
@}

The sneaky part is that instead of storing this stack explicitly on the
heap, it is stored on the run-time stack itself. This is done by having the
@{EX_BEGIN@} macro create a compound statement containing a local
automatic variable of type @{_ex_cx_t@} which forms a node of the stack.
These nodes are threaded into a linked list that is rooted by the global
variable @{_ex_curr@} which points to the node at the top of the stack,
or @{NULL@} if the stack is empty. Two other hidden global variables
@{_ex_id@} and @{_ex_info@} store the exception id and the exception
information.

@!==============================================================================

@C@<Global Variables@>

This package requires three global variables to keep track of everything.
The variable @{_ex_curr@} points to the context record on the top of the
stack, or @{NULL@} if the stack is empty. The variable @{_ex_id@} contains
a pointer to the exception most recently raised. The variable @{_ex_info@}
is provided for the use of the client.

To enable this package (and its client code) to be converted over to a
multi-threaded system at a later date, these global variables are referred
to only through a set of macros. The macros are described in the static
parameters section of this document. They are called @{_EX_CURR@},
@{EX_ID@}, @{_EX_ID@}, @{EX_INFO@}.

@$@<Global variables (.c)@>@{
#if !_EX_THRD
GLOVAR _ex_cx_t *_ex_curr = NULL;
GLOVAR p_ex_t    _ex_id;
GLOVAR ptrint    _ex_info;
#endif
@}

Here is the version of these variables that appears in the header file.

@$@<Global variables (.h)@>@{
#if !_EX_THRD
extern _ex_cx_t *_ex_curr;
extern p_ex_t    _ex_id;
extern ptrint    _ex_info;
#endif
@}

@!==============================================================================

@C@<Magic Numbers@>

In order to detect corruptions in exception context records, two magic
number fields have been placed at the start and end of the record. In C, there
are hundreds of ways for corruptions to occur, but in particular, in this
package, if a client exits an exception context without first popping it,
it is quite likely that the context will be ``run over'' by stack frames of
functions called later. If the context is then invoked, the program will
crash. The following magic number fields greatly improve the chances of
detecting such corruptions.

@$@<Header magic number field@>@{@-
#if !_EX_FAST
   ulong _ex_mag1;  /* Header magic number. */
#endif@}

@$@<Trailer magic number field@>@{@-
#if !_EX_FAST
   ulong _ex_mag2;  /* Trailer magic number. */
#endif@}

@$@<Magic number symbols@>@{
#define _EX_MAG1 0xFB8A5D30
#define _EX_MAG2 0x09F2E7A2@}

@!******************************************************************************

@B@<Exception type@>

Here is the definition of the pointer-to-exception type. Normally, a hidden
exception type would be declared to which the pointer type would point, but as
exceptions have only a single attribute, their description string, it makes
sense simply to declare the pointer type to be @{string@} type (pointer to
char).

@$@<Type p_ex_t@>@{typedef string p_ex_t;@}

@!******************************************************************************

@B@<Macros for Declaring and Defining Exceptions@>

The three exception declaration macros make use of the fact that @{p_ex_t@}
is really just a string pointer. There is a slight danger that a too-clever
C compiler might overlap two strings for which one is a non-strict suffix
of the other. However, this is considered unlikely.

@$@<Implementation of EX_EXCEPT@>@{extern const p_ex_t NAME@}
@$@<Implementation of EX_EXPORT@>@{       const p_ex_t NAME = DESC@}
@$@<Implementation of EX_LOCAL@>@{static const p_ex_t NAME = DESC@}

@!******************************************************************************

@B@<Macro for Raising Exceptions@>

Two forms of the @{EX_RAISE@} macro are given. The first simply calls
@{_exrai@} which performs lots of checks before raising the argument exception.
The fast form does not perform any checking --- it just performs the
essential operations of setting the ID and executing the @{longjmp@}.

@$@<Safe implementation of EX_RAISE(P_EX)@>@{{_exrai(P_EX);}@}

@$@<Fast implementation of EX_RAISE(P_EX)@>@{@-
{_EX_ID = (P_EX); longjmp(_EX_CURR->_ex_jmbf,NON_ZERO);}@}

@!******************************************************************************

@B@<Exception Block Macros (Safe)@>

The exception block macros are messy and deserve some explanation.
These macros operate as a coherent whole, so it is probably best to read
them through a few times to see how they interact, rather than attempting
to understand each one independently.  Two versions of the exception block
macros are given --- the safe version and the fast version. The safe version
is described first.

The @{EX_BEGIN@} macro sets the stage for the entire exception block by
starting a compound statement, declaring a local variable for the stack
node (@{_ex_cxlc@}), initializing its magic checking numbers, pushing it onto
the stack, and finally saving the current execution context in the @{_ex_jmbf@}
field of the newly created stack node. This work has to be done in this macro
as most of the operations require direct access to the local context. Not much
checking can be done here either as we have no way of cross checking the value
of @{_EX_CURR@} upon entry. The open @{if@} statement encloses the entire
normal code of the exception block.

@$@<Safe implementation of EX_BEGIN@>@{@-
{_ex_cx_t _ex_cxlc;              \
 _ex_cxlc._ex_mag1 = _EX_MAG1;   \
 _ex_cxlc._ex_mag2 = _EX_MAG2;   \
 _ex_cxlc._ex_prev = _EX_CURR;   \
 _EX_CURR          = &_ex_cxlc;  \
 if (!setjmp(_ex_cxlc._ex_jmbf)) { @}

At the point of forgetting, we need to terminate the compound statement
for the normal code, pop the current exception context and then test the
currently live exception against the candidate handlers. This is most
simply performed with a cascaded @{if@} statement. The @{if@} statement
has to start with a @{FALSE@} branch so as to establish the syntactic
context for the first @{EX_WHEN@} macro.

An earlier version of this package that used integers to represent exceptions
used a @{switch@} statement instead of an @{if@} chain. A @{switch@} statement
is actually preferable to an @{if@} chain because @{switch@}es prohibit
duplicate labels. However, @{switch@}es had to be abandoned because when the
package was changed over so that it used pointers as exception ids, it was
noticed that K&R78 section 9.7 says that the argument to @{switch@} must be of
type @{int@}. K&R88 (section A9.4) and ANSI (section 6.6.4.2) weaken this to
include all integral types, but still preclude pointers. So an @{if@} chain
had to be used.

@$@<Safe implementation of EX_FORGET@>@{;EX_POP} else {EX_POP; if (FALSE) {@}

The start of each handler in the exception block is marked by a call to the
@{EX_WHEN@} macro. All it does is add an extra clause onto the @{if@} chain.

@$@<Safe implementation of EX_WHEN(P_EX)@>@{} else if ((P_EX) == (EX_ID)) {@}

The @{EX_OTHERS@} macro is also easily implemented (but, like @{EX_FORGET@},
could conceivably provoke some inconvenient warnings from @{lint@}.

@$@<Safe implementation of EX_OTHERS@>@{} else if (TRUE) {@}

To end the construct, we terminate the current handler, re-raise the
currently live exception if it has not already been caught, and terminate
the @{else@} of @{EX_FORGET@} and the outer braces of the entire construct.

@$@<Safe implementation of EX_END@>@{} else _exrai(EX_ID); }}@}

Popping the current exception context is easily achieved with a function call.
Note: The reference to @{_ex_cxlc@} will simply not compile if there
isn't a current context. This correctly precludes the use of this macro
outside an exception block. It also guarantees that the argument to @{_expop@}
will be a pointer to the context record of the innermost statically-enclosing
exception block.

@$@<Safe implementation of EX_POP@>@{{_expop(&_ex_cxlc);}@}

@!******************************************************************************

@B@<Fast, Inline Versions of the Exception Block Macros@>

Here are the fast, inline versions of the exported macros. This version
of the macros operates in exactly the same way as the other version except
that no checking is performed whatsoever. This version of the macros
performs everything on the spot, without calling any support functions at all.

@$@<Fast implementation of EX_BEGIN@>@{@-
{_ex_cx_t _ex_cxlc;              \
 _ex_cxlc._ex_prev = _EX_CURR;   \
 _EX_CURR          = &_ex_cxlc;  \
 if (!setjmp(_ex_cxlc._ex_jmbf)) { @}

@$@<Fast implementation of EX_FORGET@>@{;EX_POP} else {EX_POP; if (FALSE) {@}
@$@<Fast implementation of EX_WHEN(P_EX)@>@{} else if ((P_EX) == (EX_ID)) {@}
@$@<Fast implementation of EX_OTHERS@>@{} else if (TRUE) {@}

@$@<Fast implementation of EX_END@>@{@-
} else longjmp(_EX_CURR->_ex_jmbf,NON_ZERO); }}@}

@$@<Fast implementation of EX_POP@>@{{_EX_CURR = _EX_CURR->_ex_prev;}@}

@!******************************************************************************

@B@<Function ex_str@>

The function @{ex_str@} is easily implemented. Because exceptions are just
pointers to strings, all it has to do is return its argument!

@$@<Function ex_str@>@{
EXPORT string ex_str (p_ex) p_ex_t p_ex; {return p_ex;}@}

@!******************************************************************************

@B@<Support Functions@>

Although most of what this package exports is macros, some of the macros
translate into calls to the support functions described in this section.
Functions have been used for operations that are particularly long-winded.

@!==============================================================================

@C@<Function _exrai@>

The function @{_exrai@} raises an exception. This function performs
all the grunt work for the @{EX_RAISE@} macro, which merely translates to a
call of this function. The function specification appears in the header file
but is not supposed to be seen by the client.

@$@<Function spec _exrai@>@{EXPORT void _exrai P_((p_ex_t));@}

@$@<Function _exrai@>@{
EXPORT void _exrai (p_ex)
p_ex_t p_ex;
{
#if _EX_FAST
 as_bomb("_exrai: This function should not be called with _EX_FAST==TRUE.");
#else
 @<Take a copy of _EX_CURR in p_curr@>
 @<Set the exception ID@>
 @<Bomb if the stack is empty@>
 @<Bomb if the target context is not on the stack@>
 @<Bomb if the target context is corrupted@>
 @<Raise the exception@>
#endif
}
@}

This function refers to the value of @{_EX_CURR@} lots of times. However,
@{_EX_CURR@} could be a function call. To avoid excessive use of this
variable, we take a copy of its value in @{p_curr@}.

@$@<Take a copy of _EX_CURR in p_curr@>@M@{_ex_cx_t * p_curr = _EX_CURR;@}

We set the global exception ID variable @{_EX_ID@} @/before@/ performing
all the sanity checks so that if one of them fails, the current exception
can be printed out by @{ex_bomb@}.

@$@<Set the exception ID@>@{_EX_ID = p_ex;@}

If the context stack is empty then there is no handler to catch the exception
and we have to bomb the program.

@$@<Bomb if the stack is empty@>@{
if (p_curr == NULL) {as_wl("_exrai: Unhandled exception."); ex_bomb();}@}

If the target context is lower down in memory than the stack frame of @/this@/
function (@{_exrai@}), then because stacks nearly always grow downwards, it
is almost certain that a context was not popped when it should have been.
Strictly speaking, this test is non-portable. However, in
practice, stacks grow downward in the address space on nearly all machines,
and it's such a powerful check, it seems worth the potential portability
problems.

@$@<Bomb if the target context is not on the stack@>@{
if (UWIDE(&p_curr) > UWIDE(&p_curr->_ex_jmbf))
  {
   as_wl("_exrai: Target exception context is no longer legitimate.");
   as_wl("        Exception context resides beneath the top of stack.");
   as_wl("        This means that earlier on, control must have left an");
   as_wl("        exception context without first popping its handler.");
   as_wl("        Look for jumps out of exception contexts that are not");
   as_wl("        immediately preceded by calls to EX_POP.");
   ex_bomb();
  }@}

Check the target context for corruptions. There is no point doing a @{longjmp@}
to a corrupted context!

@$@<Bomb if the target context is corrupted@>@{
if ((p_curr->_ex_mag1 != _EX_MAG1) || (p_curr->_ex_mag2 != _EX_MAG2))
  {
   as_wl("_exrai: Target exception context has been corrupted. This could");
   as_wl("        be because an exception context wasn't popped, or it");
   as_wl("        could be just a common garden-variety C corruption :-)");
   ex_bomb();
  }@}

Actually raising the exception is the easy part! All that is required is a
@{longjmp@}.

@$@<Raise the exception@>@{longjmp(p_curr->_ex_jmbf,NON_ZERO);@}

@!==============================================================================

@C@<Function ex_bomb@>

The function @{ex_bomb@} writes out a description of the most recently
raised exception and then bombs the program. This function is used by
@{_exrai@}.

@$@<Function ex_bomb@>@{
LOCAL void ex_bomb P_((void));
LOCAL void ex_bomb ()
{
 char s[100];
 as_wr("        Exception desc is : \""); as_wr(EX_ID); as_wl("\".");
 as_wr("        Exception id   is : ");
 sprintf(s,"%lu (= %lX)",ULONG(EX_ID),ULONG(EX_ID)); as_wl(s);
 as_wr("        Exception info is : ");
 sprintf(s,"%lu (= %lX)",ULONG(EX_INFO),ULONG(EX_INFO)); as_wl(s);
 as_bomb("Aborting program after exception error.");
}
@}

@!==============================================================================

@C@<Function _expop@>

The function @{_expop@} pops the current exception block from the context
stack. Before doing this, it performs a few checks. To assist it with its
checks, the caller must pass a pointer to the current context record
in the first parameter. This can always be done by the caller directly
(without referencing @{_EX_CURR@}) because popping is only supposed to take
place within the static context of an enclosing exception block.

@$@<Function spec _expop@>@{EXPORT void _expop P_((_ex_cx_t *));@}

@$@<Function _expop@>@{
EXPORT void _expop (p_check)
_ex_cx_t *p_check;
{
#if _EX_FAST
 as_bomb("_exrai: This function should not be called with _EX_FAST==TRUE.");
#else
 @<Take a copy of _EX_CURR in p_curr@>
 @<Bomb if the current context is not the top of stack context@>
 @<Bomb if current context is corrupted@>
 @<Zap the current context@>
 @<Pop the context@>
#endif
}
@}

The first check test is to make sure that the context about to be popped is
the current context --- the one statically enclosing the pop call.

@$@<Bomb if the current context is not the top of stack context@>@{
if (p_curr != p_check)
  {
   if (p_curr == NULL)
      as_bomb("_expop: Context stack is empty.");
   else
      as_bomb("_expop: Top of context stack is not the current context.");
  }
@}

The second check tests the current context (the one about to be popped) to
make sure that it isn't corrupted. If it is, then the program should be
bombed.

@$@<Bomb if current context is corrupted@>@{
if ((p_curr->_ex_mag1 != _EX_MAG1) || (p_curr->_ex_mag2 != _EX_MAG2))
  {
   as_wl("_ex_rai: Target exception context has been corrupted. This could");
   as_wl("         be because an exception context wasn't popped, or it");
   as_wl("         could be just a common garden-variety C corruption :-)");
   as_bomb("Aborting program after exception error.");
  }
@}

Before popping the current context, we need to zap its magic numbers so that
they don't float around in stack memory where they might cause some future
magic number test to succeed when it should have failed.

@$@<Zap the current context@>@{
p_curr->_ex_mag1 = ~_EX_MAG1;
p_curr->_ex_mag2 = ~_EX_MAG2;
@}

Having checked and zapped the context, popping it is easy!

@$@<Pop the context@>@{_EX_CURR = p_curr->_ex_prev;@}

@!******************************************************************************
@!******************************************************************************

@A@<Test Program@>

This section provides a test program which can be used to test the exceptions
package. The program provides nine success tests and nine failure tests.
The success tests do not cause the program to terminate, and so can all
be performed in a single run. However, each failure test causes the
program to bomb with an assertion error, and so the failure tests must
be performed separately as nine separate invocations of the test
program. At the end of this testing, the user can have a high degree of
confidence that the package is working.

Each of the eighteen tests is embodied in a separate function.
The global boolean variable @{flag@} is a miscellaneous temporary variable
which is usually used to tell if an exception has fired.

@$@<Test program file@>@{
@<Configuration header notice@>

#include "style.h"
#include "except.h"
#include "as.h"

EX_LOCAL(sloth_ex,"Sloth exception");
EX_LOCAL(walrus_ex,"Walrus exception");

GLOVAR bool flag;

@<Function sc01@>
@<Function sc02@>
@<Function sc03@>
@<Function sc04@>
@<Function sc05@>
@<Function sc06@>
@<Function sc07@>
@<Function sc08@>
@<Function sc09@>

@<Function fa01@>
@<Function fa02@>
@<Function fa03@>
@<Function fa04@>
@<Function fa05@>
@<Function fa06@>
@<Function fa07@>
@<Function fa08@>
@<Function fa09@>

main()
{
 char ch;

 printf("Test Program for the EXCEPT Package\n");
 printf("===================================\n");
 printf("This test program provides a number of different tests. Because\n");
 printf("some tests provoke the package to bomb on purpose, this test\n");
 printf("program must be run a number of times, once for each test.\n");
 printf("The 0 test performs a group of tests that are not supposed to\n");
 printf("fail. The 1..9 tests each perform a test that is supposed to\n");
 printf("bomb the program if the test succeeds. The 0 test should be run\n");
 printf("with the package compiled with both _EX_FAST==FALSE and\n");
 printf("_EX_FAST==TRUE. The 1..9 tests should use only _EX_FAST==FALSE.\n");
 printf("\n");
 if (_EX_FAST)
 printf("Current value of EX_FAST == TRUE.\n");
 else
 printf("Current value of EX_FAST == FALSE.\n");
 printf("\n");
 printf("Enter 0 for success tests, or 1..9 for one of nine fail tests>");
 ch=getchar(); printf("\n");
 if (ch == '0')
   {
    printf("Success Tests\n");
    printf("-------------\n");
    printf("The following tests (sc01..sc09) test the normal features\n");
    printf("the exceptions package. You should see nine success lines\n");
    printf("appear on the screen. If the package bombs during this test,\n");
    printf("then something is wrong and should be fixed.\n");
    printf("\n");
    sc01();
    sc02();
    sc03();
    sc04();
    sc05();
    sc06();
    sc07();
    sc08();
    sc09();
    printf("\n");
    printf("All of the success tests SUCCEEDED.\n");
   }
 else
   {
    if (_EX_FAST)
      {
       as_wl("Error in test configuration. An attempt was made to perform");
       as_wl("a 1..9 test with _EX_FAST==TRUE. This does not make sense as");
       as_wl("these tests test the error checking capability of the package");
       as_wl("and _EX_FAST=TRUE has all error checking turned off!");
       as_bomb("Please recompile with _EX_FAST==FALSE and try again.");
      }
    switch (ch)
     {
      case '1': fa01();
      case '2': fa02();
      case '3': fa03();
      case '4': fa04();
      case '5': fa05();
      case '6': fa06();
      case '7': fa07();
      case '8': fa08();
      case '9': fa09();
      default:
         printf("\nCharacter entered was not 0..9. Aborting...\n");
     }
    }
}
@}

@!==============================================================================

@B This test checks to make sure that exception blocks operate correctly
when no exceptions are raised.

@$@<Function sc01@>@{
LOCAL void sc01 P_((void));
LOCAL void sc01 ()
{
 printf("Test SC01: Exception block with no exceptions raised.\n");

 /* The NULL exception block should be OK. */
 EX_BEGIN
 EX_FORGET
 EX_END

 /* Now put some code in the normal section. */
 flag=FALSE;
 EX_BEGIN
    flag=TRUE;
 EX_FORGET
 EX_END
 as_cold(flag,"sc01: First flag block test failed.");

 /* Make sure the EX_OTHERS branch only applies to exceptions. */
 flag=FALSE;
 EX_BEGIN
    flag=TRUE;
 EX_FORGET
    EX_OTHERS
       as_bomb("sc01: Raise failed.");
 EX_END
 as_cold(flag,"sc01: Second flag block test failed.");
}
@}

@!==============================================================================

@B This test tests to make sure that an exception raised within an exception
block can be caught by that block.

@$@<Function sc02@>@{
LOCAL void sc02 P_((void));
LOCAL void sc02 ()
{
 printf("Test SC02: Exception caught within immediate block.\n");
 flag = FALSE;
 EX_BEGIN
    EX_RAISE(sloth_ex);
    as_bomb("sc02: Raise failed.");
 EX_FORGET
    EX_WHEN(sloth_ex) flag=TRUE;
 EX_END
 as_cold(flag,"sc02: Exception was not caught by local block.");
}
@}

@!==============================================================================

@B This test tests the @{EX_OTHERS@} feature.

@$@<Function sc03@>@{
LOCAL void sc03 P_((void));
LOCAL void sc03 ()
{
 printf("Test SC03: Exception is caught by OTHERS clause.\n");
 flag = FALSE;
 EX_BEGIN
    EX_RAISE(sloth_ex);
    as_bomb("sc03: Raise failed.");
 EX_FORGET
    EX_OTHERS flag=TRUE;
 EX_END
 as_cold(flag,"sc03: Exception was not caught by EX_OTHERS clause.");
}
@}

@!==============================================================================

@B This test tests nested exception blocks.

@$@<Function sc04@>@{
LOCAL void sc04 P_((void));
LOCAL void sc04 ()
{
 printf("Test SC04: Exception is caught by nested exception block.\n");

 /* Try a simple nested handler. */
 flag=FALSE;
 EX_BEGIN
    EX_BEGIN
       EX_RAISE(sloth_ex);
       as_bomb("sc04: Raise failed (1).");
    EX_FORGET
       EX_WHEN(walrus_ex)
          as_bomb("sc04: Walrus exception caught (1).");
    EX_END
 EX_FORGET
    EX_WHEN(sloth_ex) flag=TRUE;
    EX_WHEN(walrus_ex) as_bomb("sc04: Walrus exception caught (x).");
 EX_END
 as_cold(flag,"sc04: First nested test failed.");

 /* Test a re-raise. */
 flag=FALSE;
 EX_BEGIN
    EX_BEGIN
       EX_RAISE(sloth_ex);
       as_bomb("sc04: Raise failed (2).");
    EX_FORGET
       EX_WHEN(sloth_ex)
          EX_RAISE(walrus_ex)
          as_bomb("sc04: Raise failed (3).");
       EX_WHEN(walrus_ex)
          as_bomb("sc04: Walrus exception caught (2).");
    EX_END
 EX_FORGET
    EX_WHEN(sloth_ex)
       as_bomb("sc04: Sloth exception escaped.");
    EX_WHEN(walrus_ex)
       flag=TRUE;
 EX_END
 as_cold(flag,"sc04: Second nested test failed.");
}
@}

@!==============================================================================

@B This test tests whether @{EX_OTHERS@} can successfully re-raise an
exception.

@$@<Function sc05@>@{
LOCAL void sc05 P_((void));
LOCAL void sc05 ()
{
 STAVAR bool flag2;
 printf("Test SC05: Single OTHERS handler reraises current exception.\n");
 flag=FALSE;
 flag2=FALSE;
 EX_BEGIN
    EX_BEGIN
       EX_RAISE(sloth_ex);
       as_bomb("sc05: Raise failed (2).");
    EX_FORGET
       EX_OTHERS
          flag=TRUE;
          EX_RAISE(EX_ID);
          as_bomb("sc05: Raise failed (3).");
    EX_END
 EX_FORGET
    EX_WHEN(sloth_ex)
       flag2=TRUE;
    EX_OTHERS
       as_bomb("sc05: Exception escaped.");
 EX_END
 as_cold(flag ,"sc05: OTHERS test failed (1).");
 as_cold(flag2,"sc05: OTHERS test failed (2).");
}
@}

@!==============================================================================

@B


@$@<Function sc06@>@{
LOCAL void sc06 P_((void));
LOCAL void sc06 ()
{
 printf("Test SC06: Handlers all GOTO same code.\n");
 flag=FALSE;
 EX_BEGIN
    EX_RAISE(sloth_ex);
    as_bomb("sc06: Raise failed.");
 EX_FORGET
    EX_WHEN(sloth_ex) goto handle;
    EX_WHEN(walrus_ex)
       as_bomb("sc06: Walrus exception went off.");
       handle:
       flag=TRUE;
    EX_OTHERS
       as_bomb("sc06: Exception escaped to OTHERS.");
 EX_END
 as_cold(flag,"sc06: GOTO test failed.");
}
@}

@!==============================================================================

@B This test simply checks that EX_INFO can be written to and read from.

@$@<Function sc07@>@{
LOCAL void sc07 P_((void));
LOCAL void sc07 ()
{
 printf("Test SC07: Exercise EX_INFO.\n");
 EX_INFO = 3;
 as_cold(EX_INFO == 3,"sc07: EX_INFO test failed.");
}
@}

@!==============================================================================

@B This test simply checks that the @{ex_str@} function is working.

@$@<Function sc08@>@{
LOCAL void sc08 P_((void));
LOCAL void sc08 ()
{
 printf("Test SC08: Exercise ex_str.\n");
 as_cold(ex_str(sloth_ex) == sloth_ex,"sc08: Failed.");
}
@}

@!==============================================================================

@B Test the legitimate use of @{EX_POP@}.

@$@<Function sc09@>@{
LOCAL void sc09 P_((void));
LOCAL void sc09 ()
{
 printf("Test SC09: Test EX_POP.\n");
 flag=FALSE;
 EX_BEGIN
    EX_BEGIN
       EX_POP; goto finish;
    EX_FORGET
       EX_WHEN(sloth_ex) as_bomb("sc09: Inner catcher caught the sloth.");
    EX_END
    finish: EX_RAISE(sloth_ex);
 EX_FORGET
    EX_WHEN(sloth_ex) flag=TRUE;
 EX_END
 as_cold(flag,"sc09: Sloth exception failed to work.");
 as_cold(_EX_CURR == NULL,"sc09: EX_POP failed to pop correctly.");
}
@}

@!==============================================================================

@B This test tests that an unhandled exception will be properly caught.

@$@<Function fa01@>@{
LOCAL void fa01 P_((void));
LOCAL void fa01 ()
{
 printf("Test FA01: Unhandled Exception\n");
 printf("------------------------------\n");
 printf("This test raises the sloth exception but doesn't handle it.\n");
 printf("The result should be an \"unhandled exception\" bomb specifying\n");
 printf("the sloth exception. Here we go...\n");
 printf("\n");
 EX_RAISE(sloth_ex);
 as_wl  ("FA01 FAILED: the exception package did not catch the\n");
 as_bomb("unhandled sloth exception.\n");
}
@}

@!==============================================================================

@B This test tests two calls to @{EX_POP@}.

@$@<Function fa02@>@{
LOCAL void fa02 P_((void));
LOCAL void fa02 ()
{
 printf("Test FA02: Double EX_POP\n");
 printf("------------------------\n");
 printf("This test executes two calls to EX_POP within the normal code of\n");
 printf("an exception block. The second one should cause a\n");
 printf("\"context stack is empty\" bomb. Here we go...\n");
 printf("\n");
 EX_BEGIN
    EX_POP;
    EX_POP;
    as_bomb("FA02 FAILED: Second EX_POP did not cause bomb (1).");
 EX_FORGET
 EX_END
 as_bomb("FA02 FAILED: Second EX_POP did not cause bomb (2).");
}
@}

@!==============================================================================

@B This test tests a single call to @{EX_POP@} followed by a normal
termination of the normal code.

@$@<Function fa03@>@{
LOCAL void fa03 P_((void));
LOCAL void fa03 ()
{
 printf("Test FA03: EX_POP Followed by Normal Termination\n");
 printf("------------------------------------------------\n");
 printf("This test executes a call to EX_POP then falls through to the\n");
 printf("end of the normal code of the exception block. The result should\n");
 printf("be a \"context stack is empty\" bomb. Here we go...\n");
 printf("\n");
 EX_BEGIN
    EX_POP;
 EX_FORGET
 EX_END
 as_bomb("FA03 FAILED: Fallthough after EX_POP did not cause bomb.");
}
@}

@!==============================================================================

@B This test checks to make sure that a context can detect when it is popping
the wrong context.

@$@<Function fa04@>@{
LOCAL void fa04 P_((void));
LOCAL void fa04 ()
{
 printf("Test FA04: Exit a block without EX_POPing\n");
 printf("-----------------------------------------\n");
 printf("This test exits an exception block using a goto without first\n");
 printf("EX_POPing. The exception block is nested within another, and this\n");
 printf("test is to make sure that the outer block detects that it is\n");
 printf("popping the wrong context. The result should be a\n");
 printf("\"not current context\" bomb. Here we go...\n");
 printf("\n");
 EX_BEGIN
    EX_BEGIN
       goto there;
    EX_FORGET
    EX_END
    there:
 EX_FORGET
 EX_END
 as_bomb("FA04 FAILED: Outer block FAILED to detect stack misalignment.");
}
@}

@!==============================================================================

@B This test tests for the detection of an unpopped invalid stack context.

@$@<Function fa05@>@{
LOCAL void fa05_x P_((void));
LOCAL void fa05_x ()
{
 EX_BEGIN
    return;
 EX_FORGET
 EX_END
}

LOCAL void fa05 P_((void));
LOCAL void fa05 ()
{
 printf("Test FA05: Target Context Does Not Exist\n");
 printf("----------------------------------------\n");
 printf("This test exits an exception block from within a function using\n");
 printf("a return statement. Thus, the context remains stacked even though\n");
 printf("the enclosing C block has terminated. This test checks to see\n");
 printf("whether this problem is detected next time an exception is raised.\n");
 printf("The result should be an illegitimate context error. Here we go...\n");
 printf("\n");
 EX_BEGIN
    fa05_x();
    EX_RAISE(sloth_ex);
    as_bomb("FA05 FAILED: Failed to raise sloth.");
 EX_FORGET
    EX_WHEN(sloth_ex)
       as_bomb("FA05 FAILED: Sloth exception was raised.");
 EX_END
 as_bomb("FA05 FAILED: Costruct terminated normally - it shouldn't have.");
}
@}

@!==============================================================================

@B This test is identical to the previous one except that the test is made
at the point of exit of the outer block, not at the point of raising an
exception.

@$@<Function fa06@>@{
LOCAL void fa06 P_((void));
LOCAL void fa06 ()
{
 printf("Test FA06: Target Context Does Not Exist (Fallthrough)\n");
 printf("------------------------------------------------------\n");
 printf("This test is identical to test FA05 except that it does not\n");
 printf("raise an exception in the outer block. The result should be a\n");
 printf("\"not current context\" error. Here we go...\n");
 printf("\n");
 EX_BEGIN
    fa05_x();
 EX_FORGET
 EX_END
 as_bomb("FA06 FAILED: Costruct terminated normally - it shouldn't have.");
}
@}

@!==============================================================================

@B

@$@<Function fa07@>@{
LOCAL void fa07 P_((void));
LOCAL void fa07 ()
{
 as_bomb("Test FA07: Test FA06 is the last test.");
}
@}
@!==============================================================================

@B

@$@<Function fa08@>@{
LOCAL void fa08 P_((void));
LOCAL void fa08 ()
{
 as_bomb("Test FA08: Test FA06 is the last test.");
}
@}
@!==============================================================================

@B

@$@<Function fa09@>@{
LOCAL void fa09 P_((void));
LOCAL void fa09 ()
{
 as_bomb("Test FA09: Test FA06 is the last test.");
}
@}

@!******************************************************************************
@!******************************************************************************

@A@<Design Notes@>

This section contains a detailed design discussion of particular aspects of
this package. Inserting these notes into the implementation section would have
cluttered it up too much.

@!==============================================================================

@B@<Architectural Discussion@>

The basic engine for exceptions is provided by the ANSI standard @{setjmp@}
and @{longjmp@} primitives which provide simple non-local jumps. The challenge
is organizing them into an exception facility. To see the problems involved
in doing this, we need only focus on an executing program at the time that
an exception is raised. At this time, the exceptions package must locate
the closest handler for the particular exception. From this, two things
become immediately clear: 1) that the exceptions package is going to have to
perform ``work'' whenever an exception block is encountered as well as whenever
an exception is raised, 2) some sort of stacking of @{jmp_buf@} contexts
is required.

This reasoning suggests two organizations:

1) Maintain a stack for each exception. Whenever an exception block is
entered, push all the handlers for that block on their respective stacks.
Pop them when exiting the handler. If an exception is raised, simply look
on the top of its stack.

2) Maintain a stack of exception blocks. If an exception is raised, search
each block in turn for a matching handler.

Of these two organizations, the second is by far the better. Exceptions are
almost by definition rare events, and so in assessing the two organizations,
the emphasis should be placed on the efficiency of the ordinary case which
is the pushing and popping. The first alternative may require multiple pushes
and pops per exception block, whereas the second requires just one. There
are also a number of more practical reasons why the second alternative is
better.

Having decided that a stack of exception blocks should be maintained, the
next thing to decide is where it should be stored. The first thing that
pops to mind is to organize for the exceptions package to maintain a stack
in a linked list growing out of a global variable. While completely feasible,
this solution is distasteful as it requires a memory allocation and
deallocation per exception block instance. A far better scheme is simply
to store the stack on the run-time stack! This can be done by storing
each node of the stack in an automatic variable within the stack frame
of the function containing the exception block. These automatic variables
can then be threaded together with points to form the originally desired
explicit stack.

@!==============================================================================

@B@<Exception Identity@>

One interesting aspect of the design is the identity of exceptions.
When the client raises an exception by calling @{ex_raise@}, an argument
must be provided that in some way identifies the exception being raised.
A number of possible schemes spring to mind:

@/Integer/preprocessor constant:@/ Each exception could be mapped
onto a unique integer which would represent the exception.  These integers
could be managed by defining preprocessor symbols for them. To raise an
exception, the client would simply pass the preprocessor symbol. This solution
is simple and provides compile-time checking (if the client programmer
mistypes a number, the problem won't be discovered until the exception is
raised and not caught, but if the client mistypes a preprocessor symbol, the
compiler will issue an ``unknown symbol'' error), but it does not provide
any way to ensure that two different exceptions in two different modules
are not using the same number to represent the two different exceptions.
There are organizational solutions to this problem such as having the
programmer maintain a global list of exceptions, but these are generally
unacceptable in the long run. One other solution is to make exception
integers wide (e.g. 32-bits) and have the programmer choose the integer
for each exception by tossing a coin 32 times. The probability of a collision
would be tiny. To be sure that two exceptions were not using the same
number, exceptions could be registered with the exceptions package which
would check the uniqueness of each registered exception integer. Registration
would also allow the exception package to bind a string name to each
exception number.

@/Integer/variable:@/ The first solution is clean because it uses
preprocessor symbols. A slightly different solution is to use global variables
instead. Under this scheme, a module exporting an exception exports a global
integer variable that represents the exception. When the package starts
up, it registers the exception with the exception package. Registration in
this case is active, not passive; the exceptions package allocates an
exception number which is assigned to the variable. This solution has the
advantage of guaranteeing that exception numbers are unique. However,
exceptions often happen under strange conditions and it is probably a
disadvantage to have an exception represented by a writeable variable.

@/Pointer to record:@/ An alternative to integers is to represent
exceptions by pointers to records describing the exceptions. Each record
could be declared statically (thus ensuring that each record has a unique
pointer) and could contain a description of the exception and possibly
other useful information (e.g. a count of how often the exception has fired).
The  pointers to the records could be constants.

@/Pointer to string:@/ Pointers to records are OK, but are rather messy
to declare. As it seems that the only information that really needs to be
bound to an exception is a string describing the exception, a far better
more direct scheme is simply to represent exceptions as pointers to strings.
Each exception can be statically declared as a @{const@} pointer to a string.
This solves all the problems: 1) the exception identity is read-only,
2) the exception identity is unique (because each string is stored at
a different address), 3) the exception identity is easy to declare,
4) the exceptions package doesn't have to manage a table of exceptions,
5) the exception variable names are subject to compile-time checking.
The only disadvantages are that 1) the compiler might overlap two constant
strings if one is a non-strict suffix of the other, 2) the exception is
represented by an object that is basically a variable which could be
accidentally written-to if the program goes haywire before the exception is
raised. The @{const@} attribute helps, but may be ignored by many compilers
which will place the variable in writeable memory. These disadvantages are
considered minor compared to the advantages.

Of the solutions given above, the simplest and cleanest is the pointer to
string solution, and this is the scheme that is used in this package. This
is a particularly satisfactory solution because it solves the problem of
exception identity and exception description in one go.

@!==============================================================================

@B@<Identifier Names@>

Because it contains macros that refer to global variables, this package
exposes the major proportion of its names to the client. As such, particular
care must be taken with names.

To assist portability to brain-dead compilers, all names have generally been
restricted to six characters for external identifiers and eight for internal
identifiers. Upper case has been used for preprocessor symbols and lower case
for other identifiers. Although the use of upper case in the main exception
syntax is a little loud, it is also a continuous reminder that this is a
dangerous construct.

The menemonic for this package is @{ex@} and this appears at the beginning of
every exported symbol. This will assist to reduce symbol collisions.
In addition, any symbol that is not intended to be seen by the client has
been prepended with an underscore. This is a little bit naughty as it
contravenes the ANSI standard's rule 7.13 on identifiers reserved for
libraries (``@/All identifiers that begin with an underscore are always
reserved for use as identifiers with file scrope in both the ordinary
identifier and tag name spaces@/''), but as this package is supposed to be
a library package too, it seems only a minor breach, and completely in keeping
with the style of the ANSI library.

The names of all the exported constructs were chosen very carefully.
Because most of the exported items were macros, by universal convention,
their identifiers had to be in upper case. This requirement was not a
disadvantage though as exception handlers are so important that they ought
to be syntactically ``loud''. Prefixing each symbol with @{EX_@} is a little
verbose, but was worthwhile because it immediately binds the symbol so
tightly to this package that generic suffixes such as @{BEGIN@} can be used
as a suffix. Thus @{EX_BEGIN@} and @{EX_END@} strongly convey their identity
as the boundaries of an anonymous block, but also strongly indicate that the
block is designed to handle exceptions.

As mentioned earlier, the identifier @{EX_FORGET@} was chosen so as to
continually remind the programmer that, in the event of an exception, all
automatic variables defined within the function containing the exception block
and which have been written since the @{EX_BEGIN@} of the block, will be
undefined. This is such an important semantic point that it was considered
worthy of being embodied in the syntax too.

The hardest identifier to choose was @{EX_RAISE@}. For a long time, this
construct was simply to take the form of an exported function with the
name @{ex_rai@}. However, in the end, it was decided that it would be
better to give this facility the same syntactic feel as the other constructs.
In addition, the raising of an exception is an event that should probably
be syntactically loud. Finally @{EX_RAISE@} is more readable than @{ex_rai@}.

@!==============================================================================

@B@<Catering for Multi-Threading@>

An increasing amount of code is being run in a multi-threading environment
and it is important to ensure that all library packages (such as this one)
are compatible with multi-threading. A key requirement of multi-threading
is that there be no global variables, ar at least that all declarations
and uses of the global variables that do exist have been encapsulated in
macros that can be redefined to work with multi-threading. In this package,
the only global variables are @{_ex_curr@}, @{_ex_id@}, and @{_ex_info@}.
To simplify possible future conversion for use with a multi-threading package,
all uses of these variables have been expressed in terms of the macros
@{_EX_CURR@}, @{_EX_ID@}, @{EX_ID@}, and @{EX_INFO@}.

@!==============================================================================

@B@<Checking@>

As mentioned before, exceptions are slippery things that can easily go
wrong particularly when the exception facility is hacked together, as it
has here, out of macros and @{longjmp@}s. This section contains a list of
likely errors and failures and explains how the package copes with them.
The errors listed here are caught only in the checking version of this
package. If @{_EX_FAST@} then no checking is performed.

@/Exception has no handler:@/ This simple case is caught by the @{_exrai@}
function which will detect a @{NULL@} pointer in @{_EX_CURR@} if there
are no more exception blocks to search for handlers.

@/Client pops the context too often:@/ This case will occur if the
client calls @{EX_POP@} and then exits the exception block normally, or
if the client calls @{EX_POP@} more than once within the same exception
block. Both these errors are detected by the @{_expop@} function which
tests to ensure that the context it is being asked to pop is the context
enclosing the @{EX_POP@} call. (Note: @{EX_POP@} won't compile unless it
is called from within an exception block, so there is no chance of a
context being popped from an external function).

@/Client exits context without popping it:@/ This is the single biggest
headache, as there is no way that this error can be detected when it occurs.
There is nothing to stop the client from executing a @{return@} or explicit
@{longjmp@} from within an exception block, and if the client does, the
exception block will still be registered as the top context on the stack
of exception contexts, and control will be transferred to it if an exception
goes off, and if this happens, the program is likely to crash, as the
stack frame containing the exception block no longer exists.

This problem cannot be solved, but it can be attenuated somewhat using
the three-pronged approach adopted in this package:

1) Whenever a context is popped, a check is performed to ensure that the
context being popped is the statically enclosing exception block. If it
isn't, the program bombs. This check means that the stack will be properly
aligned following each non-illegal termination of each exception block.

2) Whenever an exception is raised, a check is performed to ensure that
the address of the context on the top of the context stack is lower than
that of the start of the stack frame of the function raising the exception.
If it is, then it must be an erroneously unpopped context.

3) Corruptions of context records are detected by having two magic number
fields in the record, one at the start of the record and one at the end.
If a record is corrupted, the corruption is likely to be detected the
next time the record is manipulated.

The last two checks do a good job of covering the two main cases that can
arise. When an exception block terminates without its context being popped,
control will often leave the function. If it does, then the top of the
run-time stack will be higher (in address value for stacks that grow down)
than the unpopped context. This is detected by the second test. If, later,
the stack grows again, it is likely to overwrite the unpopped
context, including the magic numbers it contains. If this happens, then the
error will be detected the next time an exception is raised or the stack is
popped.

Obviously, there are ways in which a computer program might satisfy all
these tests and still be erroneous, but at least these tests provide some
sort of safety net.

@!******************************************************************************

@B@<Future Improvements@>

This section gives a list of possible future improvements to this package.

* It might be useful to allow the client to specify a block of code that
is executed after an exception is raised, but before the handlers are scanned.
This would allow an opportunity for cleaning up.

* Rework the terminology so that it is more consistent.

* Hierarchical exceptions.

That's the end of this package!

@!******************************************************************************
@!*                             End of except.fw                               *
@!******************************************************************************