File: unix.cpp

package info (click to toggle)
povray 1%3A3.6.1-6
  • links: PTS
  • area: non-free
  • in suites: etch, etch-m68k
  • size: 31,052 kB
  • ctags: 20,305
  • sloc: ansic: 110,032; cpp: 86,573; sh: 13,595; pascal: 5,942; asm: 2,994; makefile: 1,747; ada: 1,637
file content (3046 lines) | stat: -rw-r--r-- 75,071 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
/****************************************************************************
 *               unix.cpp
 *
 * This module implements UNIX specific routines.
 *
 * from Persistence of Vision(tm) Ray Tracer version 3.6.
 * Copyright 1991-2003 Persistence of Vision Team
 * Copyright 2003-2004 Persistence of Vision Raytracer Pty. Ltd.
 *---------------------------------------------------------------------------
 * NOTICE: This source code file is provided so that users may experiment
 * with enhancements to POV-Ray and to port the software to platforms other
 * than those supported by the POV-Ray developers. There are strict rules
 * regarding how you are permitted to use this file. These rules are contained
 * in the distribution and derivative versions licenses which should have been
 * provided with this file.
 *
 * These licences may be found online, linked from the end-user license
 * agreement that is located at http://www.povray.org/povlegal.html
 *---------------------------------------------------------------------------
 * This program is based on the popular DKB raytracer version 2.12.
 * DKBTrace was originally written by David K. Buck.
 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
 *---------------------------------------------------------------------------
 * $File: //depot/povray/3.6-release/unix/unix.cpp $
 * $Revision: #3 $
 * $Change: 3032 $
 * $DateTime: 2004/08/02 18:43:41 $
 * $Author: chrisc $
 * $Log$
 *****************************************************************************/

/*
 * Various modifications from Mike Fleetwood, February 1999
 * In a nutshell, they allow for enhanced (and fixed) default INI support.
 * They include two new functions, UNIX_Process_Env and unix_process_povray_ini
 * and associated modifications as noted below
 */

/*
 * Multiple binaries differing only in their display glued together by
 * Mark Gordon, June 2000.
 * [NC] Mark also added the primary code for handling I/O restrictions.
 */
 
/*
 * Rewrite for 3.6, August 2003 - May 2004.
 * Basically, the whole I/O restriction code has been revisited to avoid
 * use of static buffers (and thus possible buffer overruns) and to allow
 * resolving all symbolic links.  Also changed the syntax/format of the
 * povray.conf file.
 *
 * Nicolas Calimet <pov4grasp@free.fr> [NC]
 * - Code and layout cleanup: remove deprecated stuff and improve consistency.
 *   The idea is that all UNIX_* functions are global, while all the others
 *   unix_* functions are local.
 * - moved X Window code to xwin.cpp
 * - moved SVGAlib code to svga.cpp
 * - rewrote most functions that deal with configuration/ini files.
 * - added several functions such a string manipulation.
 *
 * Christoph Hormann <chris_hormann@gmx.de> [C.H.]
 * - changing some of the text output functions.
 * - added benchmark mode.
 */


#include <ctype.h>      // for isspace()
#include <signal.h>
#include <sys/time.h>   // for gettimeofday() and time()

#ifdef UNIX_DEBUG
# include <assert.h>
#endif

#ifdef HAVE_CONFIG_H
# include "conf.h"
#else
# error "!!!!! conf.h is required !!!!!"
#endif

// source/frontend
#include "defaultrenderfrontend.h"
#include "processrenderoptions.h"
#include "defaultplatformbase.h"

// source/base
#include "stringutilities.h"  // for pov_stricmp(), pov_tsprintf() 

// source
#include "benchmark.h"
#include "povray.h"
#include "povmsgid.h"   // for kPOVObjectClass_ROptions
#include "povmsrec.h"   // for Receive_RenderOptions()
#include "userdisp.h"   // for POV_Std functions

// unix
#include "unix.h"

#ifndef X_DISPLAY_MISSING
# include "xwin.h"
#endif

#if defined(HAVE_LIBVGA) && defined(HAVE_LIBVGAGL)
# include "svga.h"
#endif


USING_POV_NAMESPACE
USING_POV_BASE_NAMESPACE
USING_POV_FRONTEND_NAMESPACE


/*****************************************************************************
 * Local preprocessor defines
 *****************************************************************************/

#ifndef TRUE
# define TRUE 1
#endif

#ifndef FALSE
# define FALSE 0
#endif

/*
 * [NC]
 * Default values for the location of the povray library and configuration.
 * These constants don't have to be in config.h .
 */
#ifndef POVLIBDIR
# define POVLIBDIR  "/usr/local/share/" PACKAGE "-" VERSION_BASE
#endif

#ifndef POVCONFDIR
# define POVCONFDIR  "/usr/local/etc/" PACKAGE "/" VERSION_BASE
#endif

#ifndef POVCONFDIR_BACKWARD
# define POVCONFDIR_BACKWARD  "/usr/local/etc"
#endif


/*****************************************************************************
* Local typedefs
******************************************************************************/

/*
 * [NC] structures to handle configuration-related (povray.conf) code.
 */
typedef struct Path     Path;
typedef struct PathList PathList;
typedef struct Config   Config;
typedef enum {IO_UNSET, IO_NONE, IO_READONLY, IO_RESTRICTED, IO_UNKNOWN} FileIO;
typedef enum {SHL_UNSET, SHL_ALLOWED, SHL_FORBIDDEN, SHL_UNKNOWN} ShellOut;

struct Path
{
  char *str;
  bool  descend, writable;
  Path *next;
};

struct PathList
{
  Path *first;
  Path *last;
};

struct Config
{
  char     *home;
  char     *sysconf;                // system conf filename
  char     *userconf;               // user conf filename
  char     *conf;                   // selected conf file
  char     *sysini,  *sysini_old;   // system ini filename
  char     *userini, *userini_old;  // user ini filename
  FileIO    file_io;
  ShellOut  shellout;
  PathList *permitted_paths;
};


/*****************************************************************************
* Local variables
******************************************************************************/

/*
 * These are used when determining which (if any) display functions are
 * to be used.
 */
static bool is_using_xwin;
static bool is_using_svga;

/*
 * Store all configuration-related data.
 */
static Config *config;
static bool no_error_call;  // workaround for early calls of Error()

/*
 * Variables used by the histogram timers
 */
#if PRECISION_TIMER_AVAILABLE
static struct timeval hstart, hstop;
#endif

/*
 * Function pointers for the POV_* , XWIN_* and SGVA_* functions.
 */
void (*UNIX_finish_povray) (void);

int  (*UNIX_display_init) (int w, int h);
void (*UNIX_display_plot) (int x, int y,
                           unsigned int Red, unsigned int Green,
                           unsigned int Blue, unsigned int Alpha);
void (*UNIX_display_plot_rect) (int x1, int y1, int x2, int y2,
                                unsigned int Red, unsigned int Green,
                                unsigned int Blue, unsigned int Alpha);
void (*UNIX_display_plot_box) (int x1, int y1, int x2, int y2,
                               unsigned int Red, unsigned int Green,
                               unsigned int Blue, unsigned int Alpha);
void (*UNIX_display_finished) (void);
void (*UNIX_display_close) (void);
int  (*UNIX_test_abort) (void);


/*****************************************************************************
* Global variables
******************************************************************************/

/*
 * For frontend handling.
 */
DefaultRenderFrontend *globalDefaultRenderFrontendPointer = NULL;


/*
 * For POVMS handling.
 */
jmp_buf globalSetJmpEnv;

extern POVMSContext POVMS_Output_Context;
extern POVMSContext POVMS_Render_Context;
extern bool         Binary_POVMS_Stream_Mode;


/*****************************************************************************
* Static functions
******************************************************************************/

// [NC]
static void unix_clear_paths (PathList *list);
#ifdef HAVE_READLINK
static char *unix_readlink (const char *path);
#endif
static char *unix_try_temp_file (const char *Filename);

/*
 * Whether we are using the X Window System or SVGAlib.
 */
static bool unix_xwin_mode (int argc, char *argv[]);
static bool unix_svga_mode (void);


/*****************************************************************************
*
* FUNCTION  unix_create_globals
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*    Allocate all global variables that need their own memory.
*
* CHANGES
*
*    New for 3.6, September 2003 [NC]
*
*****************************************************************************/

static void unix_create_globals(void)
{
  // allocate memory
  config = (Config *) POV_CALLOC(1, sizeof(Config), "Configuration");
  config->permitted_paths =
    (PathList *) POV_CALLOC(1, sizeof(PathList), "Paths");

  // home directory
  config->home = getenv("HOME");

  // Default values for I/O restrictions: everything is allowed.
  // Any restrictions must come from system or user configuration.
  config->file_io  = IO_UNSET;
  config->shellout = SHL_UNSET;

  // system configuration file
  config->conf    = NULL;
  config->sysconf = UNIX_strdup(POVCONFDIR "/povray.conf");

  // user configuration file
  config->userconf = config->home
    ? UNIX_stradd(config->home, "/." PACKAGE "/" VERSION_BASE "/povray.conf")
    : NULL
    ;

  // system ini file
  config->sysini     = UNIX_strdup(POVCONFDIR "/povray.ini");
  config->sysini_old = UNIX_strdup(POVCONFDIR_BACKWARD "/povray.ini");

  // user ini file
  config->userini = config->home
    ? UNIX_stradd(config->home, "/." PACKAGE "/" VERSION_BASE "/povray.ini")
    : NULL
    ;
  config->userini_old = config->home
    ? UNIX_stradd(config->home, "/.povrayrc")
    : NULL
    ;

#ifdef UNIX_DEBUG
  fprintf(stderr,
   "PATHS\n"
   "  HOME        = %s\n"
   "  SYSCONF     = %s\n"
   "  USERCONF    = %s\n"
   "  SYSINI      = %s\n"
   "  SYSINI_OLD  = %s\n"
   "  USERINI     = %s\n"
   "  USERINI_OLD = %s\n",
    config->home,
    config->sysconf,
    config->userconf,
    config->sysini, config->sysini_old,
    config->userini, config->userini_old
  );
#endif
}


/*****************************************************************************
*
* FUNCTION  UNIX_free_globals
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*    Free all global variables that have been allocated.
*
* CHANGES
*
*    New for 3.6, September 2003 [NC]
*
*****************************************************************************/

void UNIX_free_globals(void)
{
  if(config)
  {
    if(config->sysconf)
      POV_FREE(config->sysconf);

    if(config->userconf)
      POV_FREE(config->userconf);

    if(config->sysini)
      POV_FREE(config->sysini);
    if(config->sysini_old)
      POV_FREE(config->sysini_old);

    if(config->userini)
      POV_FREE(config->userini);
    if(config->userini_old)
      POV_FREE(config->userini_old);

    if(config->permitted_paths)
    {
      unix_clear_paths(config->permitted_paths);
      POV_FREE(config->permitted_paths);
    }

    POV_FREE(config);
  }
}


/*****************************************************************************
*
* FUNCTION  UNIX_strdup
*
* INPUT     string of nul-terminated caracters
*
* OUTPUT
*
* RETURNS   a new string with the same content
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*    Duplicate the content of a string and create a new one.
*
* CHANGES
*
*    New for 3.6, September 2003 [NC]
*
*****************************************************************************/

char *UNIX_strdup(const char *str)
{
  char   *s;
  size_t  len;

  if(! str)
    return (char *) POV_CALLOC(1, 1, "UNIX_strdup");

  len = strlen(str) + 1;
  s = (char *) POV_CALLOC(len, 1, "UNIX_strdup");

  return (char *) memcpy(s, str, len);
}


/*****************************************************************************
*
* FUNCTION  UNIX_strndup
*
* INPUT     string of nul-terminated caracters
*
* OUTPUT
*
* RETURNS   a new string with up to n caracters
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*   Duplicate the first n caracters of a string and create a new one.
*
* CHANGES
*
*   New for 3.6, September 2003 [NC]
*
*****************************************************************************/

char *UNIX_strndup(const char *str, size_t n)
{
  char   *s;
  size_t  len;

  if(! str  ||  ! n)
    return (char *) POV_CALLOC(1, 1, "UNIX_strndup");

  len = strlen(str);
  if(len > n)
    len = n;
  s = (char *) POV_MALLOC(len + 1, "UNIX_strndup");
  memcpy(s, str, len);
  s[len] = '\0';

  return s;
}


/*****************************************************************************
*
* FUNCTION  UNIX_stradd
*
* INPUT     two strings of nul-terminated caracters
*
* OUTPUT
*
* RETURNS   a new concatenated string
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*   Add the content of (concatenate) two strings and create a new one.
*
* CHANGES
*
*   New for 3.6, September 2003 [NC]
*
*****************************************************************************/

char *UNIX_stradd(const char *s1, const char *s2)
{
  char   *s;
  size_t  s1len, s2len;

  if(! s2)
    return UNIX_strdup(s1);

  s1len = (s1) ? strlen(s1) : 0;
  s2len = strlen(s2);
  s = (char *) POV_MALLOC(s1len + s2len + 1, "UNIX_stradd");

  memcpy(s, s1, s1len);
  memcpy(s + s1len, s2, s2len);
  s[s1len + s2len] = '\0';

  return s;
}


/*****************************************************************************
*
* FUNCTION  UNIX_getcwd
*
* INPUT     string containing a path or filename
*
* OUTPUT
*
* RETURNS   a new string
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*   Get the current working directory (including final slash)
*   and create a new string.
*
* CHANGES
*
*   New for 3.6, September 2003 [NC]
*
*****************************************************************************/

char *UNIX_getcwd(void)
{
  char *s, *tmp, *errormsg;

#ifdef HAVE_GETCWD
  size_t len;

  len = 256;  // default buffer size
  tmp = (char *) POV_CALLOC(len, 1, "UNIX_getcwd");

  while(getcwd(tmp, len) == NULL)  // buffer is too small
  {
    POV_FREE(tmp);
    len *= 2;  // double buffer size and try again
    tmp = (char *) POV_CALLOC(len, 1, "UNIX_getcwd");
  }
#else
  tmp = getenv("PWD");  // must not be NULL; checked by configure
  if(! tmp)             // run-time checks are safer anyway
  {
    errormsg =
      "Cannot determine the current working directory.\n"
      "Check that the PWD environment variable does exist and is valid.\n";
    if(no_error_call)
    {
      fprintf(stderr, "%s: %s\n", PACKAGE, errormsg);
      exit(EXIT_FAILURE);
    }
    else
      Error("%s", errormsg);
  }
#endif

  s = UNIX_stradd(tmp, "/");  // add final slash

#ifdef HAVE_GETCWD
  POV_FREE(tmp);
#endif

  return s;
}


/*****************************************************************************
*
* FUNCTION  unix_basename
*
* INPUT     path
*
* OUTPUT
*
* RETURNS   final filename component of the path
*           creates a new string [NC]
*
* AUTHOR    Christopher James Huff
*
* DESCRIPTION
*
*   Implementation of basename, roughly patterned after SUSv2
*
* CHANGES
*
*   Rewrite for 3.6, September 2003 [NC]
*   - input path is now constant, returns a new string.
*   - the original implementation was incomplete and had side-effects.
*
******************************************************************************/

static char *unix_basename(const char *path)
{
  int first = 0;  // index of the first caracter to keep
  int last = 0;   // index of the last caracter to keep

  if(! path)
    return UNIX_strdup("");

  first = strlen(path);
  if(first < 2)  // less than two caracters
    return UNIX_strdup(path);

  if(--first  &&  path[first] == '/')  // skip last slash
    first--;

  last = first;  // store current position
  while(first > 0  &&  path[first] != '/')  // scan the string backwards
    first--;

  if(first)
    return UNIX_strndup(path+first+1, last-first);
  else
    return UNIX_strndup(path, last+1);
}


/*****************************************************************************
*
* FUNCTION  unix_dirname
*
* INPUT     path
*
* OUTPUT
*
* RETURNS   directory component of the path
*
* AUTHOR    Christopher James Huff
*
* DESCRIPTION
*
*   Implementation of dirname, roughly patterned after SUSv2
*
* CHANGES
*
*   Rewrite for 3.6, September 2003 [NC]
*   - input path is now constant, returns a new string.
*   - the original implementation was incomplete and had side-effects.
*
******************************************************************************/

static char *unix_dirname(const char *path)
{
  int last;

  if(! path)
    return UNIX_strdup("");

  last = strlen(path);
  if(last < 2)  // less than 2 caracters
    return UNIX_strdup(path);

  if(--last  &&  path[last] == '/')  // skip last slash
    last--;

  while(last > 0  &&  path[last] != '/')  // scan the string backwards
    last--;

  return UNIX_strndup(path, last);  // last slash is skipped
}


/*****************************************************************************
*
* FUNCTION  unix_readlink
*
* INPUT     string containing a path or filename
*
* OUTPUT
*
* RETURNS   a new string
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*   Get the path for a symbolic link and create a new string.
*   A symlink is translated to its target only if the symlink is the last
*   component of the path (i.e. the basename).
*   Note: don't use the unsecure unix realpath() function.
*
* CHANGES
*
*   New for 3.6, October 2003 [NC]
*
*****************************************************************************/

#ifdef HAVE_READLINK

static char *unix_readlink(const char *path)
{
  char   *s, *tmp1, *tmp2, *tmp3;
  size_t  len;
  int     status;

  len = 256;  // default buffer size
  tmp1 = (char *) POV_CALLOC(len, 1, "unix_readlink");  // init with '\0'

  while(true)
  {
    status = readlink(path, tmp1, len-1);  // without terminating '\0'
    if(status < 0)  // an error occured, return empty string
    {
      tmp1[0] = '\0';  // for safety
      return tmp1;
    }
    else if(status == len-1)  // the buffer is probably too small
    {
      POV_FREE(tmp1);
      len *= 2;  // double buffer size and try again
      tmp1 = (char *) POV_CALLOC(len, 1, "unix_readlink");
    }
    else  // all right, let's go further
      break;
  }

  // do we have an absolute path ?
  if(tmp1[0] != '/')  // no; concatenate symlink to the path dirname
  {
    tmp2 = unix_dirname(path);
    tmp3 = UNIX_stradd(tmp2, "/");
    s = UNIX_stradd(tmp3, tmp1);
    POV_FREE(tmp1);
    POV_FREE(tmp2);
    POV_FREE(tmp3);
  }
  else  // yes; just resize buffer
  {
    s = UNIX_strdup(tmp1);
    POV_FREE(tmp1);
  }

  return s;
}

#endif  /* HAVE_READLINK */


/*****************************************************************************
*
* FUNCTION  UNIX_canonicalize_path
*
* INPUT     string containing a path or filename
*
* OUTPUT
*
* RETURNS   a new string [NC]
*
* AUTHOR    Mark Gordon <mtgordon@povray.org> July 2002
*
* DESCRIPTION
*
*   Replaces instances of . and .. in the input string.
*   Note: this is a replacement for the unsecure unix realpath() funtion [NC]
*
* CHANGES
*
*   New for 3.5
*
*   Rewrite for 3.6, September 2003 and May 2004 [NC]
*     Use dynamic strings only, in order to avoid static buffers, side effects,
*     and recursivity.  In particular, it should fix many potential buffer
*     overrun vulnerabilities (the original code was not using the strncpy and
*     strncat functions properly).  Without any side effects, this new code
*     should be really portable (provided the path seperator is '/').
*     Also added full support for symbolic links.
*
******************************************************************************/

char *UNIX_canonicalize_path(const char *path)
{
  char *s, *tmp1, *tmp2, *tmp3;
  char *match_begin, *prev_dir;
  int   i;
  typedef struct { const char *match, *replace; } subst;
  const subst strings[] = {  // beware: order does matter
    { "%INSTALLDIR%", POVLIBDIR },
    { "%HOME%", config->home },
    { "//", "/" },
    { "/./", "/" },
    { NULL, NULL }  // sentinel
  };

  // nothing to canonicalize; return an empty string
  if(! path)
    return (char *) POV_CALLOC(1, 1, "UNIX_canonicalize_path");

#ifdef UNIX_DEBUG
  fprintf(stderr, "CANONICALIZE '%s'\n", path);
#endif

  // create a copy first
  s = UNIX_strdup(path);

  // substitute all occurences of 'match' by 'replace'
  i = 0;
  while(strings[i].match)
  {
    while((match_begin = strstr(s, strings[i].match)) != NULL)
    {
      tmp1 = UNIX_strndup(s, match_begin - s);
      tmp2 = UNIX_stradd(tmp1, strings[i].replace);
      tmp3 = UNIX_stradd(tmp2, match_begin + strlen(strings[i].match));
      POV_FREE(tmp1);
      POV_FREE(tmp2);
      POV_FREE(s);
      s = tmp3;
#ifdef UNIX_DEBUG
      fprintf(stderr, "  %s\n", s);
#endif
    }
    ++i;
  }

  // substitute the current working dir to the first "./"
  // or add the cwd to the first directory or "../"
  if(! strncmp(s, "./", 2))
  {
    tmp1 = UNIX_getcwd();
    tmp2 = UNIX_stradd(tmp1, s + strlen("./"));
    POV_FREE(tmp1);
    POV_FREE(s);
    s = tmp2;
#ifdef UNIX_DEBUG
    fprintf(stderr, "  %s\n", s);
#endif
  }
  else if(s[0] != '/'  ||  ! strncmp(s, "../", 3))
  {
    tmp1 = UNIX_getcwd();
    tmp2 = UNIX_stradd(tmp1, s);
    POV_FREE(tmp1);
    POV_FREE(s);
    s = tmp2;
#ifdef UNIX_DEBUG
    fprintf(stderr, "  %s\n", s);
#endif
  }

  // iteratively translate all symlinks in the path (dirname and basename)
#ifdef HAVE_READLINK
  i = 0;
  if(s[i] == '/')
    i++;
  do
  {
    while(s[i] != '\0'  &&  s[i] != '/')
      i++;
    tmp1 = UNIX_strndup(s, i);
    tmp2 = UNIX_strdup(s + i);
    tmp3 = unix_readlink(tmp1);
    if(strlen(tmp3))  // found symlink
    {
#ifdef UNIX_DEBUG
      fprintf(stderr, "  %s -> %s\n  %s%s\n", tmp1, tmp3, tmp3, tmp2);
#endif
      POV_FREE(s);
      s = UNIX_stradd(tmp3, tmp2);
      i = 0;  // start again from beginning
      if(s[i] == '/')
        i++;
    }
    else
    {
#ifdef UNIX_DEBUG
      fprintf(stderr, "  %s\n", tmp1);
#endif
      i++;
    }
    POV_FREE(tmp1);
    POV_FREE(tmp2);
    POV_FREE(tmp3);
  } while(s[i]);
#endif  // HAVE_READLINK

  // substitute all occurences of "dir/.." by ""
  while((match_begin = strstr(s, "/..")) != NULL)
  {
    tmp1 = UNIX_strndup(s, match_begin - s);
    prev_dir = strrchr(tmp1, '/');
    tmp2 = UNIX_strndup(s, (prev_dir) ? prev_dir - tmp1 : 0);
    tmp3 = UNIX_stradd(tmp2, match_begin + strlen("/.."));
    POV_FREE(tmp1);
    POV_FREE(tmp2);
    POV_FREE(s);
    s = tmp3;
#ifdef UNIX_DEBUG
   fprintf(stderr, "  %s\n", s);
#endif
  }

  // remove the last "/." if any
  if((match_begin = strstr(s, "/.")) != NULL  &&  *(match_begin + 2) == '\0')
  {
    tmp1 = UNIX_strndup(s, match_begin - s);
    POV_FREE(s);
    s = tmp1;
#ifdef UNIX_DEBUG
    fprintf(stderr, "  %s\n", s);
#endif
  }

  return s;
}


/*****************************************************************************
*
* FUNCTION  unix_append_path
*
* INPUT     list, path (string), flag for writable, flag for descend
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*   Append a new entry to a list of paths.
*
* CHANGES
*
*   New for 3.6, February 2004 [NC]
*
******************************************************************************/

static void unix_append_path(PathList *list,
                             const char *path,
                             bool writable,
                             bool descend)
{
  Path *node;

#ifdef UNIX_DEBUG
  assert(list);
#endif

  node = (Path *) POV_MALLOC(sizeof(Path), "Path");
  node->str = (char *)path;
  node->writable = writable;
  node->descend = descend;
  node->next = NULL;

  if(! list->first)
    list->first = list->last = node;
  else
  {
    list->last->next = node;
    list->last = node;
  }
}


/*****************************************************************************
*
* FUNCTION  unix_clear_paths
*
* INPUT     list of paths
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*   Remove all entries from a list of paths.
*
* CHANGES
*
*   New for 3.6, February 2004 [NC]
*
******************************************************************************/

static void unix_clear_paths(PathList *list)
{
  Path *path, *next;

#ifdef UNIX_DEBUG
  assert(list);
#endif

  path = list->first;
  if(path) do
  {
    if(path->str)
      POV_FREE(path->str);
    next = path->next;
    POV_FREE(path);
    path = next;
  } while(path);

  list->first = list->last = NULL;
}


/*****************************************************************************
*
* FUNCTION  unix_getline
*
* INPUT     file descriptor
*
* OUTPUT
*
* RETURNS   a new string
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*   Read a line from a file and create a string.
*
* CHANGES
*
*   New for 3.6, February 2004 [NC]
*
******************************************************************************/

static char *unix_getline (FILE *f)
{
  char   *tmp;
  int     c;
  size_t  len, i;

  // inits
  i = 0;
  len = 256;  // default buffer size
  tmp = (char *) POV_CALLOC(len, 1, "unix_getline");

  // read stream
  do
  {
    c = fgetc(f);
    if(i > len-1)
    {
      len *= 2;  // double buffer size
      tmp = (char *) POV_REALLOC(tmp, len, "unix_getline");
    }
    if(c == EOF  ||  c == '\n')
    {
      tmp[i++] = '\0';  // does not include final '\n'
      return (char *) POV_REALLOC(tmp, i, "unix_getline");  // resize string
    }
    else
      tmp[i++] = c;
  } while(true);
}


/*****************************************************************************
*
* FUNCTION  unix_pre_process_line
*
* INPUT     input string
*
* OUTPUT
*
* RETURNS   a new string
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*   Create a new string from processed input.
*
* CHANGES
*
*   New for 3.6, March 2004 [NC]
*
******************************************************************************/

static char *unix_pre_process_line(const char *input) 
{
  char *begin, *end;

  begin = (char *) input;

  while(*begin  &&  isspace((int) *begin))  // skip leading spaces
    ++begin;

  end = begin;
  while(*end  &&  *end != ';')  // find comment mark
    ++end;
  if(end-begin  &&  *end == ';')
    --end;

  while(end-begin > 0  &&  isspace((int) *end))  // ignore trailing spaces
    --end;
  if(end-begin)
    ++end;

  return UNIX_strndup(begin, end-begin);
}


/*****************************************************************************
*
* FUNCTION  unix_add_permitted_path
*
* INPUT     list of paths, input string, conf filename, line number
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*   Add an entry in the list of permitted paths.
*
* CHANGES
*
*   New for 3.6, March 2004 [NC]
*
******************************************************************************/

static void unix_add_permitted_path(PathList      *list,
                                    const char    *input,
                                    const char    *conf_name,
                                    unsigned long  line_number)
{
  char *p, *directory, *begin, *tmp, quote;
  bool  descend, writable;

  // inits
  p = (char *) input;
  quote = 0;
  descend = writable = false;

  // read or read+write entry
  if(! strncmp(p, "read", strlen("read")))
  {
    p += strlen("read");
    writable = false;

    // we have a read+write path
    if(! strncmp(p, "+write", strlen("+write")))
      p += strlen("+write"), writable = true;

    // sub-directory search
    descend = false;
    if(*p == '*')
      ++p, descend = true;

    p = strchr(p, '=');  // find equal sign
    if(p)  // equal sign found
    {
      ++p;  // skip equal sign
      while(*p  &&  isspace((int) *p))
        ++p;  // skip leading spaces
      quote = 0;
      if(*p == '"'  ||  *p == '\'')
        quote = *p++;  // store and skip quote
      begin = p;
      while(*p)  // find next space caracter or closing quote
      {
        if(*p == quote  ||  (! quote  &&  isspace((int) *p)))
          break;
        ++p;
      }
      if(quote  &&  *p != quote)  // no closing quote
        fprintf(stderr,
          "%s: %s: %lu: ignored entry: missing closing %c quote.\n",
          PACKAGE, conf_name, line_number, quote
        );
      else if(p-begin)  // store given directory
      {
        directory = UNIX_strndup(begin, p-begin);
        tmp = UNIX_stradd(directory, "/");
        POV_FREE(directory);
        directory = UNIX_canonicalize_path(tmp);
        POV_FREE(tmp);
        unix_append_path(list, directory, writable, descend);
      }
      else  // nothing found after the equal sign
        fprintf(stderr,
          "%s: %s: %lu: ignored entry: missing directory.\n",
          PACKAGE, conf_name, line_number
        );
    }
    else  // equal sign not found
      fprintf(stderr,
        "%s: %s: %lu: ignored entry: missing equal sign.\n",
        PACKAGE, conf_name, line_number
      );
  }  // read or read+write entry

  // unknown entry
  else if(*p)
    fprintf(stderr,
      "%s: %s: %lu: unknown '%s' setting.\n",
      PACKAGE, conf_name, line_number, p
    );
}


/*****************************************************************************
*
* FUNCTION  unix_parse_conf_file
*
* INPUT     file descriptor and filename, flag for user mode
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Mark Gordon <mtgordon@povray.org> July 2002
*
* DESCRIPTION
*
*   Parse a povray.conf file.
*
* CHANGES
*
*   New as of July 2002
*
*   Rewrite for 3.6, September 2003 [NC]
*   - removed all internal static buffers.
*   - New and better "free format" for the povray.conf files.
*
******************************************************************************/

void unix_parse_conf_file(FILE       *conf_file,
                          const char *conf_name,
                          bool        user_mode)
{
  char           *line, *line2;
  unsigned long   line_number;
  bool            user_file_io_rejected;
  FileIO          file_io;
  bool            file_io_is_set;
  ShellOut        shellout;
  bool            shellout_is_set;
  PathList       *paths;
  short           i;

  typedef enum { NONE, FILE_IO, SHELLOUT, PERMITTED_PATHS, UNKNOWN } SectionVal;
  SectionVal section;
  typedef struct { const char *label; const SectionVal value; } Section;
  const Section sections[] =
  {
    { ""                   , NONE            },  // init
    { "[File I/O Security]", FILE_IO         },
    { "[Shellout Security]", SHELLOUT        },
    { "[Permitted Paths]"  , PERMITTED_PATHS },
    { NULL                 , UNKNOWN         }   // sentinel
  };

  typedef struct { const char *label; const FileIO value; } IOSettings;
  const IOSettings io_settings[] =
  {
    { ""          , IO_UNSET      },
    { "none"      , IO_NONE       },
    { "read-only" , IO_READONLY   },
    { "restricted", IO_RESTRICTED },
    { NULL        , IO_UNKNOWN    }
  };

  typedef struct { const char *label; const ShellOut value; } SHLSettings;
  const SHLSettings shl_settings[] =
  {
    { ""         , SHL_UNSET     },
    { "allowed"  , SHL_ALLOWED   },
    { "forbidden", SHL_FORBIDDEN },
    { NULL       , SHL_UNKNOWN   }
  };


  // inits
  line = line2 = NULL;
  line_number = 0;
  user_file_io_rejected = false;
  file_io_is_set = shellout_is_set = false;
  section = NONE;
  file_io = IO_UNSET;
  shellout = SHL_UNSET;

#ifdef UNIX_DEBUG
  fprintf(stderr, "PARSE CONF '%s'\n", conf_name);
#endif
 
  // Since the file format allows to read permitted paths before
  // setting [File I/O Security], the paths must be stored in a local
  // list which will be used only when the user setting for file I/O
  // is accepted.
  if(user_mode)  // create local list
    paths = (PathList *) POV_CALLOC(1, sizeof(PathList), "Paths");
  else  // use the config list
    paths = config->permitted_paths;

  // read in file
  while(! feof(conf_file))
  {
    // get and preprocess line
    line  = unix_getline(conf_file);
    line2 = unix_pre_process_line(line);
    POV_FREE(line);
    ++line_number;

    // skip empty line
    if(line2[0] == '\0')
    {
      POV_FREE(line2);
      continue;
    }

    // check section
    else if(line2[0] == '[')  // new section
    {
      // search section
      for(i = 0; sections[i].label; ++i)
        if(! strcmp(line2, sections[i].label))
          break;
      section = sections[i].value;

      // unknown section
      if(section == UNKNOWN)
        fprintf(stderr,
          "%s: %s: %lu: unknown '%s' section.\n",
          PACKAGE, conf_name, line_number, line2
        );
    }  // check section

    // file I/O security
    else if(section == FILE_IO)
    {
      // search setting
      for(i = 0; io_settings[i].label; ++i)
        if(! strcmp(line2, io_settings[i].label))
          break;
      file_io = io_settings[i].value;

      // multiple settings were found
      if(file_io_is_set)
        fprintf(stderr,
          "%s: %s: %lu: multiple settings for %s.\n",
          PACKAGE, conf_name, line_number, sections[section]
        );
      if(file_io != IO_UNSET)
        file_io_is_set = true;

      // unknown setting
      if(file_io == IO_UNKNOWN)
      {
        fprintf(stderr,
          "%s: %s: %lu: unknown '%s' setting for %s.  ",
          PACKAGE, conf_name, line_number, line2, sections[section]
        );
        if(user_mode)
        {
          fprintf(stderr,
            "Using system setting '%s'.\n",
            io_settings[config->file_io]
          );
          file_io = config->file_io;
          user_file_io_rejected = true;  // won't account for the user paths
        }
        else
        {
          fprintf(stderr, "I/O restrictions are disabled.\n");
          file_io = IO_NONE;
        }
      }

      // user setting not allowed
      if(user_mode  &&  file_io < config->file_io)
      {
        fprintf(stderr,
          "%s: %s: %lu: "
          "the user setting '%s' for %s is less restrictive than "
          "the system setting '%s' in '%s'.  Using system setting.\n",
          PACKAGE, conf_name, line_number,
          io_settings[        file_io].label, sections[section].label,
          io_settings[config->file_io].label, config->conf
        );
        file_io = config->file_io;
        user_file_io_rejected = true;  // won't account for the user paths
      }

      config->file_io = file_io;
    }  // file I/O security

    // shellout security
    else if(section == SHELLOUT)
    {
      // search setting
      for(i = 0; shl_settings[i].label; ++i)
        if(! strcmp(line2, shl_settings[i].label))
          break;
      shellout = shl_settings[i].value;

      // multiple settings were found
      if(shellout_is_set)
        fprintf(stderr,
          "%s: %s: %lu: multiple settings for %s.\n",
          PACKAGE, conf_name, line_number, sections[section]
        );
      if(shellout != SHL_UNSET)
        shellout_is_set = true;

      // unknown setting
      if(shellout == SHL_UNKNOWN)
      {
        fprintf(stderr,
          "%s: %s: %lu: unknown '%s' setting for %s.  ",
          PACKAGE, conf_name, line_number, line2, sections[section]
        );
        if(user_mode)
        {
          fprintf(stderr,
            "Using system setting '%s'.\n",
            shl_settings[config->shellout].label
          );
          shellout = config->shellout;
        }
        else
        {
          fprintf(stderr, "Shellout security is disabled.\n");
          shellout = SHL_ALLOWED;
        }
      }

      // user setting not allowed
      if(user_mode
      && config->shellout == SHL_FORBIDDEN
      && config->shellout != shellout)
      {
        fprintf(stderr,
          "%s: %s: %lu: "
          "the user setting '%s' for %s is less restrictive than "
          "the system '%s' setting in '%s'.  Using system setting.\n",
          PACKAGE, conf_name, line_number,
          shl_settings[        shellout].label, sections[section].label,
          shl_settings[config->shellout].label, config->conf
        );
        shellout = config->shellout;
      }

      config->shellout = shellout;
    }  // shellout security

    // permitted paths
    else if(section == PERMITTED_PATHS)
      unix_add_permitted_path(paths, line2, conf_name, line_number);

    // cleanup
    POV_FREE(line2);
  }  // read in file

#ifdef UNIX_DEBUG
  fprintf(stderr,
    "I/O RESTRICTIONS\n"
    "  file_io  = %d\tconfig->file_io  = %d\n"
    "  shellout = %d\tconfig->shellout = %d\n",
    file_io, config->file_io,
    shellout, config->shellout
  );
#endif

  // assign user settings and paths
  if(user_mode)
  {
    if(user_file_io_rejected)
    {
      unix_clear_paths(paths);  // free all user paths assigned here
      POV_FREE(paths);
      fprintf(stderr,
        "%s: %s: user permitted paths are ignored.  Using system paths.\n",
        PACKAGE, conf_name
      );
    }
    else
    {
      unix_clear_paths(config->permitted_paths);  // clear existing paths
      POV_FREE(config->permitted_paths);
      config->permitted_paths = paths;            // assign new paths
    }
  }
}


/*****************************************************************************
*
* FUNCTION  unix_process_povray_conf
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Mark Gordon <mtgordon@povray.org>
*
* DESCRIPTION
*
*   Process povray.conf files in the following search order:
*     1) $PREFIX/etc/povray/3.6/povray.conf  ($PREFIX defaults to /usr/local)
*     2) $HOME/.povray/3.6/povray.conf
*   There is no backward compatibility for conf files as the format changed.
*
* CHANGES
*
*   New for 3.5, July 2002.
*
*   Rewrite for 3.6, September 2003 [NC]
*
******************************************************************************/

static void unix_process_povray_conf(void)
{
  FILE *f;

  // read system configuration file
  if(config->sysconf)
  {
    f = fopen(config->sysconf, "r");
    if(f)
    {
      unix_parse_conf_file(f, config->sysconf, false);
      fclose(f);
      config->conf = config->sysconf;
    }
    else
    {
      fprintf(stderr,
        "%s: cannot open the system configuration file ",
        PACKAGE
      );
      perror(config->sysconf);
    }
  }

  // read user configuration file
  if(config->userconf)
  {
    f = fopen(config->userconf, "r");
    if(f)
    {
      unix_parse_conf_file(f, config->userconf, true);
      fclose(f);
      config->conf = config->userconf;
    }
    else
    {
      fprintf(stderr,
        "%s: cannot open the user configuration file ",
        PACKAGE
      );
      perror(config->userconf);
    }
  }

  // no file was read, disable I/O restrictions
  if(! config->conf)
    fprintf(stderr, "%s: I/O restrictions are disabled.\n", PACKAGE);

  // if no paths specified, at least include POVLIBDIR and POVCONFDIR
  else if(! config->permitted_paths->first)
  {
    unix_append_path(config->permitted_paths,
      UNIX_stradd(POVLIBDIR, "/"), false, true   // read*
    );
    unix_append_path(config->permitted_paths,
      UNIX_stradd(POVCONFDIR,"/"), false, false  // read
    );
  }

#ifdef UNIX_DEBUG
  {
    Path *path;

    fprintf(stderr, "PERMITTED PATHS\n");
    path = config->permitted_paths->first;
    if(path) do
    {
      fprintf(stderr,
        "  %s%s = \"%s\"\n",
        path->writable ? "WRITE" : "READ", path->descend ? "*" : "", path->str
      );
      path = path->next;
    } while(path);
  }
#endif
}


/*****************************************************************************
*
* FUNCTION  unix_parse_ini_file
*
* INPUT     filename
*
* OUTPUT
*
* RETURNS   error code
*
* AUTHOR    Mark Gordon <mtgordon@povray.org>
*
* DESCRIPTION
*
*   Parse INI file.
*
* CHANGES
*
*   New for 3.5 - copied from Windows version.
*
*   Rewrite for 3.6, November 2003 [trf]
*
*****************************************************************************/

static int unix_parse_ini_file(const char            *filename,
                               ProcessRenderOptions&  renderopts,
                               POVMSObjectPtr         renderoptsobject)
{
  int err = 0;

  try
  {
    if(filename  &&  EXIST_FILE(filename) == false)
      err = kCannotOpenFileErr;
    else
    {
#ifdef UNIX_DEBUG
      fprintf(stderr, "PARSE INI '%s'\n", filename);
#endif
      err = renderopts.ParseFile(filename, renderoptsobject);
    }
  }
  catch(int e)
  {
    err = e;
  }
  catch(...)
  {
    err = -1;
  }

  return err;
}


/*****************************************************************************
*
* FUNCTION  unix_process_povray_ini
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*   Process INI files in the following search order:
*     1) $POVINI
*     2) ./povray.ini
*     3) $HOME/.povray/3.6/povray.ini
*     4) $PREFIX/etc/povray/3.6/povray.ini  ($PREFIX defaults to /usr/local)
*     5) $HOME/.povrayrc                    (backward compatibility with 3.5)
*     6) $PREFIX/etc/povray.ini             (backward compatibility with 3.5)
*
* CHANGES
*
*   Created by Mike Fleetwood  Jan 1999
*
*   Rewrite for 3.6, September 2003 [NC]
*
******************************************************************************/

static void unix_process_povray_ini(ProcessRenderOptions& renderopts,
                                    POVMSObjectPtr        renderoptsobject)
{
  char *povini;
  FILE *f;

  // try the file pointed to by POVINI
  povini = getenv("POVINI");
  if(povini)
  {
    // returns 0 when the file is parsed successfully
    if(! unix_parse_ini_file(povini, renderopts, renderoptsobject))
      return;
    else
    {
      fprintf(stderr, "%s: POVINI environment: cannot open ", PACKAGE);
      perror(povini);
    }
  }

  // try any INI file in the current directory
  povini = "./povray.ini";
  if(! unix_parse_ini_file(povini, renderopts, renderoptsobject))
    return;

  // try the user or system INI file
  if(config->home
  && ! unix_parse_ini_file(config->userini, renderopts, renderoptsobject))
    return;
  if(! unix_parse_ini_file(config->sysini, renderopts, renderoptsobject))
    return;

  // try older user or system INI files
  if(config->home
  && ! unix_parse_ini_file(config->userini_old, renderopts, renderoptsobject))
    return;
  if(! unix_parse_ini_file(config->sysini_old, renderopts, renderoptsobject))
    return;

  // warn that no INI file was found and add minimal library_path setting
  povini = unix_try_temp_file("pov36.ini");
  if(strlen(povini))
  {
    fprintf(stderr,
      "%s: cannot open an INI file, creating a temporary file '%s'.\n",
      PACKAGE, povini
    );
    f = fopen(povini, "w");
    if(f)
    {
      fprintf(f, "Library_Path=%s/include", POVLIBDIR);
      fclose(f);
      unix_parse_ini_file(povini, renderopts, renderoptsobject);
      fprintf(stderr, "%s: removing '%s'.\n", PACKAGE, povini);
      DELETE_FILE(povini);
    }
    else
    {
      fprintf(stderr, "%s: cannot write to ", PACKAGE);
      perror(povini);
    }
  }
  POV_FREE(povini);

  return;
}


/*****************************************************************************
*
* FUNCTION  unix_subdir
*
* INPUT     Filename (canonicalized !), system or user paths
*
* OUTPUT
*
* RETURNS   a boolean, telling whether the file is in any of the
*           directories in the list (or their subdirectories).
*
* AUTHOR    Mark Gordon <mtgordon@povray.org> July 2002
*
* DESCRIPTION
*
*   Checks to see whether the file requested is in a directory
*   specified in either POVCONFDIR/povray.conf or $HOME/.povray.conf
*   or in a subdirectory of any of the dirs in the list.
*
* CHANGES
*
*   Security bug fixed October 2002
*
*   Rewrite for 3.6, September 2003 [NC]
*   - removed all internal static buffers.
*   - subdirectories are not systematically allowed.
*   - added write parameter.
*
******************************************************************************/

static bool unix_subdir (const char *Filename, PathList *list, bool write)
{
  int   i;
  char *dirname, *pathname;
  Path *path;

#ifdef UNIX_DEBUG
  assert(list);
#endif

  // NOTE: Filename must be already canonicalized

  // no filename nor paths
  if(! Filename  ||  ! list->first)
    return false;

  // scan the list of paths
  path = list->first;
  do
  {
    if(path->descend)  // allows sub-directories
    {
      if(! write  ||  path->writable)
      {
#ifdef UNIX_DEBUG
        fprintf(stderr,
          "  FILE '%s' <-> %s* '%s'\n",
          Filename, path->writable ? "WRITE" : "READ", path->str
        );
#endif
        if(! strncmp(Filename, path->str, strlen(path->str)))  // match found
        {
#ifdef UNIX_DEBUG
          fprintf(stderr, "  OK\n");
#endif
          return true;
        }
      }
    }
    else  // check for exact match with path->str (without last slash)
    {
      dirname = unix_dirname(Filename);
      pathname = UNIX_strndup(path->str, strlen(path->str) - 1);
      if(! write  ||  path->writable)
      {
#ifdef UNIX_DEBUG
        fprintf(stderr,
          "  DIRNAME '%s' <-> %s '%s'\n",
          dirname, path->writable ? "WRITE" : "READ", pathname
        );
#endif
        if(! strcmp(dirname, pathname))
        {
          POV_FREE(dirname);
          POV_FREE(pathname);
#ifdef UNIX_DEBUG
          fprintf(stderr, "  OK\n");
#endif
          return true;
        }
        POV_FREE(dirname);
        POV_FREE(pathname);
      }
    }
    path = path->next;
  } while(path);

#ifdef UNIX_DEBUG
  fprintf(stderr, "  BAD\n");
#endif

  return false;
}


/*****************************************************************************
*
* FUNCTION  UNIX_allow_file_read
*
* INPUT     Filename, FileType
*
* OUTPUT
*
* RETURNS   TRUE if file reading is allowed, else FALSE
*
* AUTHOR    Mark Gordon <mtgordon@povray.org> July 2002
*
* DESCRIPTION
*
*    Performs tests to determine whether file reading is allowed.
*
* CHANGES
*
*    Brand new, March 2002 [mtg]
*
*    Rewrite for 3.6, September 2003 [NC]
*    - removed internal static buffer.
*    - added comments and code cleaning.
*    - added support for the write_paths (which are also readable).
*    - removed support for current working directory (changed .conf format).
*
******************************************************************************/

int UNIX_allow_file_read (const char *Filename, const unsigned int FileType)
{
  char       *filename;
  const char *errormsg;

  // read is always possible in these cases
  if(IO_RESTRICTIONS_DISABLED  ||  config->file_io < IO_RESTRICTED)
    return (TRUE);
  if(! Filename)
    return (FALSE);

#ifdef UNIX_DEBUG
  fprintf(stderr, "READ '%s'\n", Filename);
#endif

  // canonicalize Filename and create a new string
  filename = UNIX_canonicalize_path(Filename);

  // check against read-only paths or read/write paths
  if(unix_subdir(filename, config->permitted_paths, false))
  {
    POV_FREE(filename);
    return (TRUE);
  }

  // bad luck
  else
  {
    errormsg = pov_tsprintf(
      "Reading from '%s' is not permitted.  Check the configuration in '%s'.",
      filename, config->conf
    );
    POV_FREE(filename);
    UNIX_free_globals();
    if(no_error_call)
    {
      fprintf(stderr, "%s: %s\n", PACKAGE, errormsg);
      exit(EXIT_FAILURE);
    }
    else
      Error("%s", errormsg);
  }

  return (FALSE);
}


/*****************************************************************************
*
* FUNCTION  UNIX_allow_file_write
*
* INPUT     Filename, FileType
*
* OUTPUT
*
* RETURNS   TRUE if file writing is allowed, else FALSE
*
* AUTHOR    Mark Gordon <mtgordon@povray.org> July 2002
*
* DESCRIPTION
*
*    Performs tests to determine whether file writing is allowed.
*
* CHANGES
*
*    Brand new, March 2002 [mtg]
*
*    Rewrite for 3.6, September 2003 [NC]
*    - removed internal static buffer.
*    - added comments and code cleaning.
*    - removed support for current working directory (changed .conf format).
*
******************************************************************************/

int UNIX_allow_file_write (const char *Filename, const unsigned int FileType)
{
  char       *filename;
  const char *errormsg;

  // binary built without I/O restrictions, everything is allowed
  if(IO_RESTRICTIONS_DISABLED)
    return (TRUE);
  if(! Filename)
    return (FALSE);

#ifdef UNIX_DEBUG
  fprintf(stderr, "WRITE '%s'\n", Filename);
#endif

  // canonicalize Filename and create a new string
  filename = UNIX_canonicalize_path(Filename);

  // try to write to the config->sysconf or config->userconf files
  // so terminate with Error()
  if((config->sysconf   &&  ! strcmp(config->sysconf, filename))
  || (config->userconf  &&  ! strcmp(config->userconf, filename)))
  {
    errormsg = pov_tsprintf("Writing to '%s' is not permitted.\n", filename);
    POV_FREE(filename);
    UNIX_free_globals();
    if(no_error_call)
    {
      fprintf(stderr, "%s: %s\n", PACKAGE, errormsg);
      exit(EXIT_FAILURE);
    }
    else
      Error("%s", errormsg);
    return (FALSE);  // not used, here just for clarity
  }

  // can write everywhere
  else if(config->file_io < IO_READONLY)
  {
    POV_FREE(filename);
    return (TRUE);
  }

  // file is in a permitted directory
  else if(unix_subdir(filename, config->permitted_paths, true))
  {
    POV_FREE(filename);
    return (TRUE);
  }

  // bad luck
  else
  {
    errormsg = pov_tsprintf(
      "Writing to '%s' is not permitted.  Check the configuration in '%s'.\n",
      filename, config->conf
    );
    POV_FREE(filename);
    UNIX_free_globals();
    if(no_error_call)
    {
      fprintf(stderr, "%s: %s\n", PACKAGE, errormsg);
      exit(EXIT_FAILURE);
    }
    else
      Error("%s", errormsg);
  }

  return (FALSE);
}


/*****************************************************************************
*
* FUNCTION  unix_try_temp_file
*
* INPUT     Filename
*
* OUTPUT
*
* RETURNS   A new string containing the full path and filename
*           or an empty string otherwise
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*    Tries to locate a directory from the permitted list of paths to
*    write a temporary file.
*
* CHANGES
*
*    New for 3.6, May 2004 [NC]
*
******************************************************************************/

static char *unix_try_temp_file (const char *Filename)
{
  char *filename, *basename, *tmp;
  Path *path = NULL;

  if(! Filename)
    return UNIX_strdup("");

#ifdef UNIX_DEBUG
  fprintf(stderr, "TRY '%s'\n", Filename);
#endif

  // canonicalize Filename and create a new string
  filename = UNIX_canonicalize_path(Filename);

  // binary built without I/O restrictions, everything is allowed
  if(IO_RESTRICTIONS_DISABLED)
    return filename;

  // get the basename of the original (uncanonicalized) Filename
  basename = unix_basename(Filename);

  if(config->permitted_paths)
    path = config->permitted_paths->first;

  do
  {
    // try to write to the config->sysconf or config->userconf files
    if((config->sysconf   &&  ! strcmp(config->sysconf, filename))
    || (config->userconf  &&  ! strcmp(config->userconf, filename)))
    {
#ifdef UNIX_DEBUG
      fprintf(stderr, "  BAD\n");
#endif
      ;  // do nothing
    }

    // can write everywhere, or file is in a permitted directory
    else if(config->file_io < IO_READONLY
         || unix_subdir(filename, config->permitted_paths, true))
    {
      POV_FREE(basename);
      return filename;
    }

    // search next writable path
    if(path) do
    {
      if(path->writable)
        break;
      path = path->next;
    } while(path);

    // no path, return empty string
    if(! path)
    {
      POV_FREE(basename);
      POV_FREE(filename);
#ifdef UNIX_DEBUG
      fprintf(stderr, "  BAD\n");
#endif
      return UNIX_strdup("");
    }

    // create new filename
    POV_FREE(filename);
    tmp = UNIX_stradd(path->str, basename);
#ifdef UNIX_DEBUG
    fprintf(stderr, "TRY '%s'\n", tmp);
#endif
    filename = UNIX_canonicalize_path(tmp);
    POV_FREE(tmp);
    path = path->next;
  } while(true);

  POV_FREE(basename);

  return filename;
}


/*****************************************************************************
*
* FUNCTION  UNIX_system
*
* INPUT     wrapper for system(3)
*
* OUTPUT
*
* RETURNS   Return value of child process, or failure if it's not allowed.
*
* AUTHOR    Mark Gordon <mtgordon@povray.org> July 2002
*
* DESCRIPTION
*
*    Passes the string to system(3) if and only if system calls are allowed.
*
* CHANGES
*
*    Brand new, July 2002 [mtg]
*
*    Updated for 3.6, September 2003 [NC]
*    - code simplified.
*    - changed the warning level to 350 (i.e. version > 3.5).
*
******************************************************************************/

int UNIX_system (const char *string)
{
  if(IO_RESTRICTIONS_DISABLED  ||  config->shellout < SHL_FORBIDDEN)
    return (system(string));
  else
    Warning(350, "Shellout not allowed under your configuration.");

  return (-1);
}


/*****************************************************************************
*
* FUNCTION  unix_xwin_mode
*
* INPUT     argc, argv - the command-line arguments
*
* OUTPUT
*
* RETURNS   TRUE or FALSE (whether to use X preview functions)
*
* AUTHOR    mtgordon@povray.org <Mark Gordon>
*
* DESCRIPTION
*
*    Determines whether preview should use functions from the
*    X Window System.  It assumes you want X preview if you're
*    currently in X (as determined by checking for the presence
*    of the $DISPLAY environment variable) or if you're passing
*    -display on the command line.  Unless I'm mistaken, one of these
*    has to be true in order for you to use X-based preview.
*
*    If you don't have X installed (as determined by the configure script),
*    this is just going to return false.  Since the official binary
*    will depend on X to run, you'll need to recompile if you don't have X.
*
* CHANGES
*
*    Brand-new, June 2000. [mtg]
*
******************************************************************************/

static bool unix_xwin_mode (int argc, char *argv[])
{
#ifndef X_DISPLAY_MISSING
  int index;

  /* if $DISPLAY is non-null, return true */
  if(getenv("DISPLAY") != NULL)
    return (true);

  /* if passing -display, return true */
  for(index = 1; index < argc; index++)
    if(!strncmp(argv[index], "-display\0", 9))
      return (true);

#endif /* X_DISPLAY_MISSING */

  return (false);
}


/*****************************************************************************
*
* FUNCTION  unix_svga_mode
*
* INPUT
*
* OUTPUT
*
* RETURNS   TRUE or FALSE (whether to use SVGALib display functions)
*
* AUTHOR    mtgordon@povray.org <Mark Gordon>
*
* DESCRIPTION
*
*   Determines whether preview should use SVGAlib functions.
*   It assumes you want SVGA preview if you are at a TTY and either
*   you're root or POV-Ray is running suid root.  I've tried my best
*   to reduce the risk of buffer-overflow exploits for anyone who wants
*   to run POV-Ray suid root, but I still feel like I'm playing with fire.
*   Run suid root at your own risk.
*
*   If you don't have SVGAlib installed (as determined by the configure
*   script), this is just going to return false.  Since the official binary
*   will depend on SVGAlib to run, you'll need to recompile if you don't
*   SVGAlib.
*
* CHANGES
*
*   Brand-new as of June 2000.  Let me know if you find any problems.
*
*   Updated for 3.6, October 2003
*   - added test for ttyname()  [C.H.]
*   - moved test for is_using_xwin out of here  [NC]
*
******************************************************************************/

static bool unix_svga_mode (void)
{
#if defined(HAVE_LIBVGA) && defined(HAVE_LIBVGAGL)
  /* C.H. ttyname() might return NULL - this needs to be caught */
  if(ttyname(0) == NULL)
    return (false);

  /* if this isn't actually a TTY, no need for SVGA */
  if(!strstr(ttyname(0), "tty"))
    return (false);

  /* if POV-Ray is setuid-root, proceed */
  if(geteuid() == 0)
    return (true);

  /* or if the user is actually root, proceed */
  if(getuid() == 0)
    return (true);

  /*
   * if display is turned off, revert to non-SVGA, otherwise non-root users
   * won't be able to run it from the console.
   */
  if(!(opts.Options & DISPLAY))
  {
    UNIX_finish_povray     = &POV_Std_Finish_Povray;
    UNIX_display_init      = &POV_Std_Display_Init;
    UNIX_display_plot      = &POV_Std_Display_Plot;
    UNIX_display_plot_rect = &POV_Std_Display_Plot_Rect;
    UNIX_display_plot_box  = &POV_Std_Display_Plot_Box;
    UNIX_display_finished  = &POV_Std_Display_Finished;
    UNIX_display_close     = &POV_Std_Display_Close;
    UNIX_test_abort        = &POV_Std_Test_Abort;
    return (false);
  }
#endif  /* HAVE_LIBVGA && HAVE_LIBVGAGL */

  return (false);
}


/*****************************************************************************
*
* FUNCTION  unix_get_command_line
*
* INPUT     pointers to program arguments
*
* OUTPUT
*
* RETURNS   the new value of argc after arguments modifications
*
* AUTHOR    Nicolas Calimet
*
* DESCRIPTION
*
*   This is the function used to define the GETCOMMANDLINE macro (config.h).
*   The purpose is to process the command line before it is parsed by
*   the main POV-Ray code.  This pre-processing is required under X-Windows.
*   See the XWIN_init_povray() function (in xwin.cpp).  Note that since
*   we define our own main() function, the GETCOMMANDLINE macro is actually
*   unused.
*
* CHANGES
*
*   New for 3.6, September 2003 [NC]
*   - taken from UNIX_init_povray()
*
*****************************************************************************/

static int unix_get_command_line (int *argc, char **argv[])
{
  is_using_xwin = unix_xwin_mode(*argc, *argv);
  if(is_using_xwin)
#ifndef X_DISPLAY_MISSING
    XWIN_init_povray(argc, argv);  // this changes the command-line args
#else
    ;  // do nothing
#endif
  else
  {
    is_using_svga = unix_svga_mode();
    if(is_using_svga)
#if defined(HAVE_LIBVGA) && defined(HAVE_LIBVGAGL)
      SVGA_init_povray();
#else
      ;  // do nothing
#endif
  }

  return *argc;
}


/*****************************************************************************
*
* FUNCTION  UNIX_startup_povray
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Mark Gordon <mtgordon@povray.org>
*
* DESCRIPTION
*
*   Performs X, SVGA, or ASCII initialization (using function pointers).
*
* CHANGES
*
*   Brand new, June 2000 [mtg]
*
*   Updated for 3.6, September 2003 [NC]
*   - renamed from UNIX_init_povray().
*   - code cleanup and a few commented changes.
*   - removed most of the static buffers.
*   - moved the I/O restrictions calls elsewhere.
*
******************************************************************************/

void UNIX_startup_povray (void)
{
  /*
   * Assume these and override as needed.
   */
  UNIX_finish_povray     = &POV_Std_Finish_Povray;
  UNIX_display_init      = &POV_Std_Display_Init;
  UNIX_display_plot      = &POV_Std_Display_Plot;
  UNIX_display_plot_rect = &POV_Std_Display_Plot_Rect;
  UNIX_display_plot_box  = &POV_Std_Display_Plot_Box;
  UNIX_display_finished  = &POV_Std_Display_Finished;
  UNIX_display_close     = &POV_Std_Display_Close;
  UNIX_test_abort        = &POV_Std_Test_Abort;

  /*
   * If we have X available to us, find out if we want to use it.
   * We'll use it if we're currently in an X session or if we're
   * passing -display on the command line.  If we decide that here,
   * set appropriate function counters to reduce later calculation.
   * [NC] unix_xwin_mode() and XWIN_init_povray() moved out of here. 
   */
  if(is_using_xwin)
  {
#ifndef X_DISPLAY_MISSING  /* [NC] moved inside the if condition */
    UNIX_finish_povray     = &XWIN_finish_povray;
    UNIX_display_init      = &XWIN_display_init;
    UNIX_display_plot      = &XWIN_display_plot;
    UNIX_display_plot_rect = &XWIN_display_plot_rect;
    UNIX_display_plot_box  = &XWIN_display_plot_box;
    UNIX_display_finished  = &XWIN_display_finished;
    UNIX_display_close     = &XWIN_display_close;
    UNIX_test_abort        = &XWIN_test_abort;
#endif  /* X_DISPLAY_MISSING */
  }

  /*
   * If we have SVGA available to us, find out if we want to use it.
   * We must be at the console, and we must either be root or running
   * suid root.
   * [NC] unix_svga_mode() moved out of here.
   */
  else if(is_using_svga)  // [NC] added else
  {
#if defined(HAVE_LIBVGA) && defined(HAVE_LIBVGAGL)  // [NC] moved in here
    UNIX_finish_povray     = &SVGA_finish_povray;
    UNIX_display_init      = &SVGA_display_init;
    UNIX_display_plot      = &SVGA_display_plot;
    UNIX_display_plot_rect = &SVGA_display_plot_rect;
    UNIX_display_plot_box  = &SVGA_display_plot_box;
    UNIX_display_finished  = &SVGA_display_finished;
    UNIX_display_close     = &SVGA_display_close;
    UNIX_test_abort        = &SVGA_test_abort;
#endif  /* HAVE_LIBVGA && HAVE_LIBVGAGL */
  }

  /* 
   * Insert other display types here (e.g. frame buffer)
   * if you feel like it.
   */
  else
  {
  }

  return;
}


/*****************************************************************************
*
* FUNCTION  POV_Std_Finish_Povray
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Mark Gordon <mtgordon@povray.org>
*
* DESCRIPTION
*
*   Just a dummy function to satisfy the function pointer.
*
* CHANGES
*
*   Brand-new, June 2000. [mtg]
*
******************************************************************************/

void POV_Std_Finish_Povray(void)
{
  return;
}


/*****************************************************************************
*
* FUNCTION  POV_Std_Test_Abort
*
* INPUT
*
* OUTPUT
*
* RETURNS   0
*
* AUTHOR    Mark Gordon <mtgordon@povray.org>
*
* DESCRIPTION
*
*   Just a dummy function to satisfy the function pointer.
*
* CHANGES
*
*   Brand-new, June 2000. [mtg]
*
******************************************************************************/

int POV_Std_Test_Abort(void)
{
  /*
   * This is the render abort function.
   * It is not needed for the command-line interface,
   * since the abort is handled via interrupts.
   */
  return (0);
}


/*****************************************************************************
*
* FUNCTION  matherr
*
* INPUT     pointer to exception
*
* OUTPUT
*
* RETURNS   1
*
* AUTHOR
*
* DESCRIPTION
*
*   The error handler for floating point exceptions.  Not totally critical,
*   but can avoid aborting a trace that gets a floating-point error during
*   the render.  It tries to return a reasonable value for the errors.
*   However, this slows down the rendering noticably.
*
*   Note: this is a SystemVism, so don't be surprised if this doesn't work
*   on BSDish systems.  It shouldn't be critical, and it should still compile.
*
* CHANGES
*
******************************************************************************/

#ifdef UNDERFLOW
#ifdef exception

int matherr(struct exception *x)
{
  switch(x->type)
  {
    case DOMAIN:
    case OVERFLOW:
      x->retval = 1.0e17;
      break;
    case SING:
    case UNDERFLOW:
      x->retval = 0.0;
      break;
  }

  return (1);
}

#endif /* exception  */
#endif /* UNDERFLOW  */


#if PRECISION_TIMER_AVAILABLE

/*****************************************************************************
*
* FUNCTION  UNIX_timer_start
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Andreas Dilger   Oct 1995
*
* DESCRIPTION
*
*   Starts the histogram timer.
*
* CHANGES
*
******************************************************************************/

void UNIX_timer_start(void)
{
  gettimeofday(&hstart, (struct timezone *)NULL);
}


/*****************************************************************************
*
* FUNCTION  UNIX_timer_stop
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Andreas Dilger   Oct 1995
*
* DESCRIPTION
*
*   Stops the histogram timer.
*
* CHANGES
*
******************************************************************************/

void UNIX_timer_stop(void)
{
  gettimeofday(&hstop, (struct timezone *)NULL);
}


/*****************************************************************************
*
* FUNCTION  UNIX_timer_count
*
* INPUT
*
* OUTPUT
*
* RETURNS   the elapsed real time between start and stop in 10^-5s increments
*
* AUTHOR    Andreas Dilger   Oct 1995
*
* DESCRIPTION
*
*   The elapsed wall-clock time between the last two calls to
*   accumulate_histogram is measured in tens of microseconds.
*   Using microseconds for the histogram timer would only allow
*   1.2 hours per histogram bucket (ie 2^32 x 10^-6s in an unsigned
*   32 bit long), which may not be enough in some rare cases :-)
*   Note that since this uses wall-clock time rather than CPU
*   time, the histogram will have noise in it because of the
*   task switching going on.
*
* CHANGES
*
******************************************************************************/

int UNIX_timer_count(void)
{
  return ((hstop.tv_sec - hstart.tv_sec) * 100000 +
          (hstop.tv_usec - hstart.tv_usec + 5) / 10);
}


#endif  /* PRECISION_TIMER_AVAILABLE */


/*****************************************************************************
*
* FUNCTION  UNIX_abort_start
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Andreas Dilger   Oct 1995
*
* DESCRIPTION
*
*   Initializes the signal handlers.
*
* CHANGES
*
*   Apr 1996: Add SIGFPE to be ignored, bacause of floating point exceptions.
*             This isn't a great solution, but I don't know of another way to
*             prevent core dumps on Linux systems that use older versions of
*             libm. [AED]
*
******************************************************************************/

void UNIX_abort_start(void)
{
#ifdef SIGTERM
  signal(SIGTERM, UNIX_abort_handler);
#endif /* SIGTERM */
#ifdef SIGINT
  signal(SIGINT, UNIX_abort_handler);
#endif /* SIGINT */
#ifdef SIGPIPE
  signal(SIGPIPE, UNIX_abort_handler);
#endif /* SIGPIPE */
#if !defined(OVERFLOW) && defined(SIGFPE) /* avoid floating point exceptions */
  signal(SIGFPE, SIG_IGN);
#endif /* !defined(OVERFLOW) && defined (SIGPFE) */
}


/*****************************************************************************
*
* FUNCTION  UNIX_abort_handler
*
* INPUT     signum - signal number, or 0 if not called by a signal
*
* OUTPUT
*
* RETURNS
*
* AUTHOR    Andreas Dilger   Oct 1995
*
* DESCRIPTION
*
*   This is the signal handler.  If it is called by a signal (signal > 0),
*   then it will call set the global "Stop_Flag", and the rendering will
*   be aborted when the current pixel is complete.  We re-initialize the
*   signal handler for those older systems that reset the signal handlers
*   when they are called.  If we are really quitting (signal = 0) or this
*   routine has been called many times, we set the signal handlers back to
*   their default action, so that further signals are handled normally.
*   This is so we don't get caught in a loop if we are handling a signal
*   caused by the shutdown process (ie file output to a pipe that's closed).
*
* CHANGES
*
*   Sept 1, 1996   Don't write to stderr if there have been many interrupts,
*                  so we don't write to a PIPE that's closed and cause more
*                  interrupts. [AED]
*
******************************************************************************/

void UNIX_abort_handler(int signum)
{
  UNIX_abort_start();

  if(signum > 0 && ++Stop_Flag < 5)
  {
    switch(signum)
    {
#ifdef SIGINT
      case SIGINT:
        PossibleError("Got %d SIGINT.", Stop_Flag);
        break;
#endif /* SIGINT  */
#ifdef SIGPIPE
      case SIGPIPE:
        PossibleError("Got %d SIGPIPE.", Stop_Flag);
        break;
#endif /* SIGPIPE  */
#ifdef SIGTERM
      case SIGTERM:
        PossibleError("Got %d SIGTERM.", Stop_Flag);
        break;
#endif /* SIGTERM  */
      default:
        PossibleError("Got %d unknown signal (%d).", Stop_Flag, signum);
        break;
    }
  }

  if(signum == 0 || Stop_Flag > 10)
  {
#ifdef SIGTERM
    signal(SIGTERM, SIG_DFL);
#endif /* SIGTERM  */
#ifdef SIGINT
    signal(SIGINT, SIG_DFL);
#endif /* SIGINT  */
#ifdef SIGPIPE
    signal(SIGPIPE, SIG_DFL);
#endif /* SIGPIPE  */
  }
}


/*****************************************************************************
*
* FUNCTION  main
*
* INPUT     program arguments
*
* OUTPUT
*
* RETURNS   program result
*
* AUTHOR    Thorsten Froehlich [trf]
*
* DESCRIPTION
*
*   Main function based on November 2003 povray.cpp main function.
*
* CHANGES
*
*   Created Nov. 13, 2003 [trf]
*
*****************************************************************************/

int main(int argc, char **argv)
{
  USING_POV_NAMESPACE
  USING_POV_BASE_NAMESPACE
  USING_POV_FRONTEND_NAMESPACE

  DefaultPlatformBase platformbase;
  POVMSAddress addr = POVMSInvalidAddress;
  int err = kNoErr;
  int ret = 0;
  int i = 0;
  bool Benchmark_Mode=false;
  char *command_line;
  char *demo_ini_name;
  char *demo_file_name;
  int len;
  char s[3];

  // Init
  no_error_call = true;
  povray_init();

  // unix-specific inits
  is_using_xwin = is_using_svga = false;
  unix_create_globals();
 
  if(err == kNoErr)  // get render context address
    err = POVMS_GetContextAddress(POVMS_Render_Context, &addr);
  if(err != kNoErr)
    (void)POVMS_ASSERT_OUTPUT(
      "Accessing POVMS render context failed.",
      "unix.cpp",
      0
    );

  if(err == kNoErr)  // create local frontend context
    err = POVMS_OpenContext(&POVMS_Output_Context);
  if(err != kNoErr)
    (void)POVMS_ASSERT_OUTPUT(
      "Creating POVMS output context failed.",
      "unix.cpp",
      0
    );
  else
  {
    DefaultRenderFrontend frontend(POVMS_Output_Context, addr);

    globalDefaultRenderFrontendPointer = &frontend;

    unix_process_povray_conf();
    unix_get_command_line(&argc, &argv);

    // Binary control mode via POVMS on stdin and stdout
    if((argc > 1) && (pov_stricmp(argv[1], "-povms") == 0))
      Binary_POVMS_Stream_Mode = true;

    // benchmark mode
    if(argc > 1) 
    if((pov_stricmp(argv[1], "-benchmark") == 0)
    || (pov_stricmp(argv[1], "--benchmark") == 0))
    {
      i = Get_Benchmark_Version() ;
      fprintf(stderr, "\
%s: entering the standard POV-Ray 3.6 benchmark version %x.%02x.\n\
Running the benchmark will take some time, e.g. about 45 minutes on\n\
a 2 GHz Intel Pentium 4 (tm) processor.  There will be no display or\n\
file output.  To get an accurate benchmark result you might consider\n\
running POV-Ray with 'nice'.\n\n\
Press <Enter> to continue or CTRL+C to abort.\n\
",
        PACKAGE, i / 256, i % 256
      );
      // don't change this to getchar() - otherwise <Enter> will also 
      // influence commands after povray finishes. [C.H.]
      fgets(s, 2, stdin); 

      demo_ini_name = unix_try_temp_file("pov36_benchmark.ini");
      demo_file_name = unix_try_temp_file("pov36_benchmark.pov");
      if(Write_Benchmark_File(demo_file_name, demo_ini_name))
      {
        fprintf(stderr, "%s: creating '%s'.\n", PACKAGE, demo_file_name);
        fprintf(stderr, "%s: creating '%s'.\n", PACKAGE, demo_ini_name);
        len = strlen(demo_ini_name) + strlen(demo_file_name) + 100 ;
        command_line = (char *) POV_MALLOC(len, "benchmark render");
        sprintf(command_line,
          "Include_Ini='%s' Input_File_Name='%s'",
          demo_ini_name, demo_file_name
        );
        fprintf(stderr,
          "Running standard POV-Ray benchmark version %x.%02x\n",
          i / 256, i % 256
        );
      }
      else
      {
        fprintf(stderr,
          "%s: failed to write temporary files for benchmark\n",
          PACKAGE
        );
        exit(EXIT_FAILURE);
      }

      Benchmark_Mode=true;
    }

    // Print help screens
    if(argc == 1)
    {
      povray_cooperate();
      frontend.PrintHelpScreens();
        return 0;
    }
    else if(argc == 2)
    {
      if((pov_stricmp(argv[1], "-h") == 0)
      || (pov_stricmp(argv[1], "-?") == 0)
      || (pov_stricmp(argv[1], "--help") == 0)
      || (pov_stricmp(argv[1], "-help") == 0))
      {
        povray_cooperate();
        frontend.PrintHelpScreens();
          return 0;
      }
      else if(argv[1][0] == '-')
      {
        if(argv[1][1] == '?')
        {
          frontend.PrintUsage(argv[1][2] - '0');
            return 0;
        }
        else if(strlen(argv[1]) == 6)
        {
          if(((argv[1][1] == 'h') || (argv[1][1] == 'H'))
          && ((argv[1][2] == 'e') || (argv[1][2] == 'E'))
          && ((argv[1][3] == 'l') || (argv[1][3] == 'L'))
          && ((argv[1][4] == 'p') || (argv[1][4] == 'P')))
          {
            frontend.PrintUsage(argv[1][5] - '0');
            return 0;
          }
        }
      }
    }  // argc == 2

    // Binary control mode via POVMS on stdin and stdout
    if((argc > 1) && (pov_stricmp(argv[1], "-povms") == 0))
      Binary_POVMS_Stream_Mode = true;

    if(Binary_POVMS_Stream_Mode == true)
    {
      while(Cooperate_Render_Flag >= 0)
        povray_cooperate();
      return 0;
    }

    try
    {
      ProcessRenderOptions renderoptions;
      POVMSObject obj;
      int l = 0;

      // create obj, a regular POVMS object that holds render options
      err = POVMSObject_New(&obj, kPOVObjectClass_ROptions);
      if(err != kNoErr)
        throw err;

      // get render options from some INI file
      unix_process_povray_ini(renderoptions, &obj);

      // benchmark mode: just parse the prepared command line
      if(Benchmark_Mode)
      {
        err = renderoptions.ParseString(command_line, &obj, false);
        if(err != kNoErr)
          throw err;
      }
      else
        // get the command-line options
        for(i = 1; i < argc; i++)
          if(pov_stricmp(argv[i], "-povms") != 0)
          {
            err = renderoptions.ParseString(argv[i], &obj, true);
            if(err != kNoErr)
              throw err;
          }

      // write all these options to a new INI file
      if(POVMSUtil_GetStringLength(&obj, kPOVAttrib_CreateIni, &l) == kNoErr)
      {
        char *outputini = new char[l];
        if(POVMSUtil_GetString(&obj, kPOVAttrib_CreateIni, outputini, &l)
           == kNoErr)
          renderoptions.WriteFile(outputini, &obj);
      }

      no_error_call = false;

      // give obj to optionsobj, a POVMS C++ wrapper needed for frontend */
      POVMS_Object optionsobj(obj);
      frontend.StartRender(optionsobj);  // start rendering

      // wait until the render is complete
      while(frontend.GetState() != RenderFrontend::kReady)
        povray_cooperate();
    }

    // catch exceptions
    catch(int err)
    {
      fprintf(stderr, "Failed to render file due to error(s)!\n");
      return err;
    }
    catch(const char *str)
    {
      fprintf(stderr, "%s\n Failed to render file!\n", str);
      return -1;
    }
    catch(...)
    {
      fprintf(stderr, "Failed to render file due to error(s)!\n");
      return -1;
    }

    // benchmark mode - cleanup
    if(Benchmark_Mode)
    {
      fprintf(stderr, "%s: removing '%s'.\n", PACKAGE, demo_file_name);
      fprintf(stderr, "%s: removing '%s'.\n", PACKAGE, demo_ini_name);
      DELETE_FILE(demo_file_name) ;
      DELETE_FILE(demo_ini_name) ;
      POV_FREE(demo_file_name);
      POV_FREE(demo_ini_name);
      POV_FREE(command_line);
    }

    // NOTE: It is important that 'frontend' be destroyed in this block scope
    // because 'POVMS_CloseContext' will destroy the render context too early
    // otherwise!
  }  // err == kNoErr

  // Finish
  povray_terminate();

  (void)POVMS_CloseContext(POVMS_Output_Context);

  return ret;
}