[0001] [0002] [0003] [0004] [0005] [0006] [0007] [0008] [0009] [0010] [0011] [0012] [0013] [0014] [0015] [0016] [0017] [0018] [0019] [0020] [0021] [0022] [0023] [0024] [0025] [0026] [0027] [0028] [0029] [0030] [0031] [0032] [0033] [0034] [0035] [0036] [0037] [0038] [0039] [0040] [0041] [0042] [0043] [0044] [0045] [0046] [0047] [0048] [0049] [0050] [0051] [0052] [0053] [0054] [0055] [0056] [0057] [0058] [0059] [0060] [0061] [0062] [0063] [0064] [0065] [0066] [0067] [0068] [0069] [0070] [0071] [0072] [0073] [0074] [0075] [0076] [0077] [0078] [0079] [0080] [0081] [0082] [0083] [0084] [0085] [0086] [0087] [0088] [0089] [0090] [0091] [0092] [0093] [0094] [0095] [0096] [0097] [0098] [0099] [0100] [0101] [0102] [0103] [0104] [0105] [0106] [0107] [0108] [0109] [0110] [0111] [0112] [0113] [0114] [0115] [0116] [0117] [0118] [0119] [0120] [0121] [0122] [0123] [0124] [0125] [0126] [0127] [0128] [0129] [0130] [0131] [0132] [0133] [0134] [0135] [0136] [0137] [0138] [0139] [0140] [0141] [0142] [0143] [0144] [0145] [0146] [0147] [0148] [0149] [0150] [0151] [0152] [0153] [0154] [0155] [0156] [0157] [0158] [0159] [0160] [0161] [0162] [0163] [0164] [0165] [0166] [0167] [0168] [0169] [0170] [0171] [0172] [0173] [0174] [0175] [0176] [0177] [0178] [0179] [0180] [0181] [0182] [0183] [0184] [0185] [0186] [0187] [0188] [0189] [0190] [0191] [0192] [0193] [0194] [0195] [0196] [0197] [0198] [0199] [0200] [0201] [0202] [0203] [0204] [0205] [0206] [0207] [0208] [0209] [0210] [0211] [0212] [0213] [0214] [0215] [0216] [0217] [0218] [0219] [0220] [0221] [0222] [0223] [0224] [0225] [0226] [0227] [0228] [0229] [0230] [0231] [0232] [0233] [0234] [0235] [0236] [0237] [0238] [0239] [0240] [0241] [0242] [0243] [0244] [0245] [0246] [0247] [0248] [0249] [0250] [0251] [0252] [0253] [0254] [0255] [0256] [0257] [0258] [0259] [0260] [0261] [0262] [0263] [0264] [0265] [0266] [0267] [0268] [0269] [0270] [0271] [0272] [0273] [0274] [0275] [0276] [0277] [0278] [0279] [0280] [0281] [0282] [0283] [0284] [0285] [0286] [0287] [0288] [0289] [0290] [0291] [0292] [0293] [0294] [0295] [0296] [0297] [0298] [0299] [0300] [0301] [0302] [0303] [0304] [0305] [0306] [0307] [0308] [0309] [0310] [0311] [0312] [0313] [0314] [0315] [0316] [0317] [0318] [0319] [0320] [0321] [0322] [0323] [0324] [0325] [0326] [0327] [0328] [0329] [0330] [0331] [0332] [0333] [0334] [0335] [0336] [0337] [0338] [0339] [0340] [0341] [0342] [0343] [0344] [0345] [0346] [0347] [0348] [0349] [0350] [0351] [0352] [0353] [0354] [0355] [0356] [0357] [0358] [0359] [0360] [0361] [0362] [0363] [0364] [0365] [0366] [0367] [0368] [0369] [0370] [0371] [0372] [0373] [0374] [0375] [0376] [0377] [0378] [0379] [0380] [0381] [0382] [0383] [0384] [0385] [0386] [0387] [0388] [0389] [0390] [0391] [0392] [0393] [0394] [0395] [0396] [0397] [0398] [0399] [0400] [0401] [0402] [0403] [0404] [0405] [0406] [0407] [0408] [0409] [0410] [0411] [0412] [0413] [0414] [0415] [0416] [0417] [0418] [0419] [0420] [0421] [0422] [0423] [0424] [0425] [0426] [0427] [0428] [0429] [0430] [0431] [0432] [0433] [0434] [0435] [0436] [0437] [0438] [0439] [0440] [0441] [0442] [0443] [0444] [0445] [0446] [0447] [0448] [0449] [0450] [0451] [0452] [0453] [0454] [0455] [0456] [0457] [0458] [0459] [0460] [0461] [0462] [0463] [0464] [0465] [0466] [0467] [0468] [0469] [0470] [0471] [0472] [0473] [0474] [0475] [0476] [0477] [0478] [0479] [0480] [0481] [0482] [0483] [0484] [0485] [0486] [0487] [0488] [0489] [0490] [0491] [0492] [0493] [0494] [0495] [0496] [0497] [0498] [0499] [0500] [0501] [0502] [0503] [0504] [0505] [0506] [0507] [0508] [0509] [0510] [0511] [0512] [0513] [0514] [0515] [0516] [0517] [0518] [0519] [0520] [0521] [0522] [0523] [0524] [0525] [0526] [0527] [0528] [0529] [0530] [0531] [0532] [0533] [0534] [0535] [0536] [0537] [0538] [0539] [0540] [0541] [0542] [0543] [0544] [0545] [0546] [0547] [0548] [0549] [0550] [0551] [0552] [0553] [0554] [0555] [0556] [0557] [0558] [0559] [0560] [0561] [0562] [0563] [0564] [0565] [0566] [0567] [0568] [0569] [0570] [0571] [0572] [0573] [0574] [0575] [0576] [0577] [0578] [0579] [0580] [0581] [0582] [0583] [0584] [0585] [0586] [0587] [0588] [0589] [0590] [0591] [0592] [0593] [0594] [0595] [0596] [0597] [0598] [0599] [0600] [0601] [0602] [0603] [0604] [0605] [0606] [0607] [0608] [0609] [0610] [0611] [0612] [0613] [0614] [0615] [0616] [0617] [0618] [0619] [0620] [0621] [0622] [0623] [0624] [0625] [0626] [0627] [0628] [0629] [0630] [0631] [0632] [0633] [0634] [0635] [0636] [0637] [0638] [0639] [0640] [0641] [0642] [0643] [0644] [0645] [0646] [0647] [0648] [0649] [0650] [0651] [0652] [0653] [0654] [0655] [0656] [0657] [0658] [0659] [0660] [0661] [0662] [0663] [0664] [0665] [0666] [0667] [0668] [0669] [0670] [0671] [0672] [0673] [0674] [0675] [0676] [0677] [0678] [0679] [0680] [0681] [0682] [0683] [0684] [0685] [0686] [0687] [0688] [0689] [0690] [0691] [0692] [0693] [0694] [0695] [0696] [0697] [0698] [0699] [0700] [0701] [0702] [0703] [0704] [0705] [0706] [0707] [0708] [0709] [0710] [0711] [0712] [0713] [0714] [0715] [0716] [0717] [0718] [0719] [0720] [0721] [0722] [0723] [0724] [0725] [0726] [0727] [0728] [0729] [0730] [0731] [0732] [0733] [0734] [0735] [0736] [0737] [0738] [0739] [0740] [0741] [0742] [0743] [0744] [0745] [0746] [0747] [0748] [0749] [0750] [0751] [0752] [0753] [0754] [0755] [0756] [0757] [0758] [0759] [0760] [0761] [0762] [0763] [0764] [0765] [0766] [0767] [0768] [0769] [0770] [0771] [0772] [0773] [0774] [0775] [0776] [0777] [0778] [0779] [0780] [0781] [0782] [0783] [0784] [0785] [0786] [0787] [0788] [0789] [0790] [0791] [0792] [0793] [0794] [0795] [0796] [0797] [0798] [0799] [0800] [0801] [0802] [0803] [0804] [0805] [0806] [0807] [0808] [0809] [0810] [0811] [0812] [0813] [0814] [0815] [0816] [0817] [0818] [0819] [0820] [0821] [0822] [0823] [0824] [0825] [0826] [0827] [0828] [0829] [0830] [0831] [0832] [0833] [0834] [0835] [0836] [0837] [0838] [0839] [0840] [0841] [0842] [0843] [0844] [0845] [0846] [0847] [0848] [0849] [0850] [0851] [0852] [0853] [0854] [0855] [0856] [0857] [0858] [0859] [0860] [0861] [0862] [0863] [0864] [0865] [0866] [0867] [0868] [0869] [0870] [0871] [0872] [0873] [0874] [0875] [0876] [0877] [0878] [0879] [0880] [0881] [0882] [0883] [0884] [0885] [0886] [0887] [0888] [0889] [0890] [0891] [0892] [0893] [0894] [0895] [0896] [0897] [0898] [0899] [0900] [0901] [0902] [0903] [0904] [0905] [0906] [0907] [0908] [0909] [0910] [0911] [0912] [0913] [0914] [0915] [0916] [0917] [0918] [0919] [0920] [0921] [0922] [0923] [0924] [0925] [0926] [0927] [0928] [0929] [0930] [0931] [0932] [0933] [0934] [0935] [0936] [0937] [0938] [0939] [0940] [0941] [0942] [0943] [0944] [0945] [0946] [0947] [0948] [0949] [0950] [0951] [0952] [0953] [0954] [0955] [0956] [0957] [0958] [0959] [0960] [0961] [0962] [0963] [0964] [0965] [0966] [0967] [0968] [0969] [0970] [0971] [0972] [0973] [0974] [0975] [0976] [0977] [0978] [0979] [0980] [0981] [0982] [0983] [0984] [0985] [0986] [0987] [0988] [0989] [0990] [0991] [0992] [0993] [0994] [0995] [0996] [0997] [0998] [0999] [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]
/*****************************************************************************/ /* FORMwork.c A scripting utility that validates a POSTed form's fields against requirements stored in the form and associated with each field. Any anomalies are reported to the client. When validated correctly the form contents can have any or all of the following processing performed: o raw request body be placed in an individual time-stamped file o processed form be placed in an individual time-stamped file o form field values appended as CSV (comma-separated variable) file record o processed form emailed to a specified address o CLI global symbols created containing form field values Some FORMwork functionality parallels that of the tMAILer utility. Generally it provides more form-related processing. In particular the CSV functionality allows results easily to be imported into other data processing utilities. The FORMwork directives are embedded in the HTML <FORM> inside each <INPUT>, <TEXTAREA> or <SELECT> tag as an associated FORMWORK=".." elements. This is ignore by the client agent (browser) and eliminates the need for any sort of 'template' file and keeps the validation data tightly associated with the form field data. The HTML file is read directly by FORMwork and these FORMWORK=".." elements used to validate the submitted form field values. The FORMWORK=".." directives are as follows: AS as-is (by default CR/LFs are replaced by a space) CG CGI environment variable value DE{sss} descriptive string (for reporting errors) EM email field ('name@host') IG ignore this field (not stored in CSV, etc., appears in CLI symbols) LI{sss} literal string (prefixes any value string) MA mandatory field NU numeric (digit only) field OP optional field (complementary to mandatory) SInnn maximum size (number of characters) WA formwork "watch" mode FORMwork directives are basically two character upper or lower case keywords, some with curly-brace delimited strings as parameters, others with trailing digits. Any non-alphanumeric characters can be used inbetween directives to make them more readable. Each <INPUT>/<TEXTAREA> tag must have a HTML NAME=".." element identifying it and a FORMWORK=".." element associated with it. Where particular form components such as TYPE=RADIO can have multiple <INPUT>s with the same NAME=".." at least one must have a FORMWORK=".." associated with it. Example HTML containing <FORM> tag: <form method="POST" enctype="form/url-encoded" action="/cgi-bin/test.com"> <input type="text" size="60" name="field1" formwork="MASI60DE{some example text}"> <input type="radio" name="field2" value="one"> <input type="radio" name="field2" value="two"> <input type="radio" name="field2" value="three" formwork="MA DE{some example text}"> <input type="checkbox" name="field3" value="four" formwork="OP"> <input type="checkbox" name="field4" value="five" formwork="OP"> <input type="checkbox" name="field5" value="six" formwork="OP"> <input type="text" name="field6" formwork="MA NU SI6 DE{Field Six}"> <select name="field7" formwork="ma de{From this selection}"> <option value="seven">7 <option value="eight">8 <option value="nine">9 </select> <input type="submit"> </form> When successfully submitted a small default 'success' response is provided by default. To redirect to some other response use the /LOCATION=url qualifier. To suppress all FORMwork responses and have the wrapper DCL procedure generate a response use the /NORESPONSE qualifier. FORMwork exits with a NORMAL status when successfully submitted or an inhibited ERROR status when not. The /SYMBOLS qualifier can be used to generate global symbols from the form field names and values. Values that cannot be accomodated by the current VMS version are truncated without warning (basically 255 earlier than V7.0, 1023 for V7.0 through to V7.3-1, or 8191 for V7.3-2 and later). The raw and processed file names are also provided as CLI symbols. Wrapper procedures can use these symbols during post-processing. A <INPUT> type="submit" (submit button) can optionally have a name="..", value=".." and associated formwork=".." element. This allows the wrapper script to detect one of possibly multiple submit buttons. WATCH MODE ---------- A developer 'debug' mode is available allowing the various stages of processing, and some configuration error messages where necessary, to be viewed as plain text. This is enabled using the 'WA' directive, the /WATCH qualifier, and the FORMWORK$WATCH logical name. QUALIFIERS ---------- /CAPACITY=integer set maximum capacity of field value in Kbytes (default 4) /CHARSET=string explicitly specify a "text/" response character set (to suppress just supply an empty string) /CSV=filename output CSV file name record to be appended to /DBUG turns on all "if (Debug)" statements /DOUBLESPACE double-space the lines of human-readable response /EMAIL=string email address for mailing human-readable response /HTML=filename input file containing the <form> /LOCATION=url successful submission redirection URL /NORESPONSE suppress success response, let wrapper procedure do it /PERSONAL_NAME=string mail message VMS personal name /PROCESSED=filename output file name containing human-readable response /RAW=filename output file name for copy of URL-form-encoded request /SEPARATOR=char specifies CSV separator character (default is TAB) /SOFTWAREID display the FORMWORK version /SUBJECT=string mail message subject line /SYMBOLS[=string] create CLI symbols from form data /VERSION display the FORMWORK version /WATCH enable "watch" mode (same as 'WA' and FORMWORK$WATCH) LOGICAL NAMES ------------- FORMWORK$DBUG turns on all "if (Debug)" statements FORMWORK$PARAM equivalent to (overrides) the command line parameters/qualifiers (define as a system-wide logical) FORMWORK$WATCH if this logical value is the same as the /HTML= string then the equivalent of the 'WA' directive is enabled BUILD DETAILS ------------- See BUILD_FORMWORK.COM procedure. COPYRIGHT --------- Copyright (C) 2004-2021 Mark G.Daniel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. VERSION HISTORY (update SOFTWAREVN as well!) --------------- 12-FEB-2006 MGD v1.1.0, /capacity 04-AUG-2004 MGD v1.0.0, initial development */ /*****************************************************************************/ #define SOFTWAREVN "1.1.0" #define SOFTWARENM "FORMWORK" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN #endif #ifdef __VAX # define SOFTWAREID SOFTWARENM " VAX-" SOFTWAREVN #endif #ifdef __x86_64 # define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN #endif /* standard C header files */ #include <ctype.h> #include <errno.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unixio.h> /* VMS-related header files */ #include <descrip.h> #include <jpidef.h> #include <libclidef.h> #include <maildef.h> #include <ssdef.h> #include <stsdef.h> #include <syidef.h> #define boolean int #define BOOL int #define true 1 #define false 0 /* application related header file */ #include "[-.misc]cgilib.h" char CopyrightInfo [] = "Copyright (C) 2004-2006 Mark G.Daniel.\n\ This software comes with ABSOLUTELY NO WARRANTY.\n\ This is free software, and you are welcome to redistribute it\n\ under the conditions of the GNU GENERAL PUBLIC LICENSE, version 2."; #ifndef __VAX # pragma nomember_alignment #endif #define VMSok(x) ((x) & STS$M_SUCCESS) #define VMSnok(x) (!((x) & STS$M_SUCCESS)) #define FI_LI "FORMWORK", __LINE__ #define QUIET_ERROR_EXIT STS$M_INHIB_MSG | STS$K_ERROR #define FORM_URLENCODED "application/x-www-form-urlencoded" #define WATCH_DIVIDER "+++++++++++++++" char *MonthName [] = { "", "Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec" }; char Utility [] = "FORMWORK"; #define MAX_FIELDS 255 typedef struct FieldStruct FIELD_STRUCT; struct FieldStruct { BOOL AsIsCarCon, EmailField, IgnoreField, MandatoryField, NumericField; int MaxSize; char *DescPtr, *FormWorkPtr, *NamePtr, *ValuePtr; }; FIELD_STRUCT FieldData [MAX_FIELDS]; BOOL Debug, CliDoubleSpace, CliNoResponse, FormWorkWatch; int BodyBufferCount, FieldCount, FieldValueCapacity, FormLength, PrintfReportLength, PrintfStringLength, ScriptPid; unsigned long BinTime [2]; unsigned short NumTime [7]; char *BodyBufferPtr, *CharsetPtr, *CgiContentTypePtr, *CgiEnvironmentPtr, *CgiRequestMethodPtr, *CliCharsetPtr, *CliCsvFileNamePtr, *CliEmailAddressPtr, *CliHtmlFileNamePtr, *CliLocationPtr, *CliPersonalNamePtr, *CliProcessedFileNamePtr, *CliRawFileNamePtr, *CliSeparatorPtr = "\t", *CliSubjectPtr, *CliSymbolPtr = "FORMWORK_", *FormPtr, *PrintfReportPtr, *PrintfStringPtr; char CharsetString [64], ContentTypeCharset [64], SoftwareID [48], SourceHost [256]; char ErrorBufferOverflow [] = "Buffer overflow.", ErrorConfig [] = "FormWork configuration error. Contact the site administrator.", ErrorHttpMethod [] = "HTTP method must be \'GET\' or \'POST\'!", ErrorReadingFile [] = "reading HTML file.", ErrorMemory [] = "allocating memory.", ErrorNotUrlEncoded [] = "Must be URL-encoded form data!", ErrorWritingFile [] = "writing data file.", SuccessMessage [] = "Successfully submitted. Thankyou."; char ReportMandatory [] = "\'%s\' must be supplied.", ReportNotEmail [] = "\'%s\' must be in the format \'user@host\'.", ReportNotNumeric [] = "\'%s\' must contain only a number.", ReportTooLarge [] = "\'%s\' must contain a maximum of %d characters."; char ReportToClient [] = "The following information must be corrected before proceding.\n\ <p><ol>\n%s</ol>\n\ <p>Go back to the form, make the changes, then submit again. Thankyou.\n"; /* required function prototypes */ void PrintfReport (char*, ...); void PrintfString (char*, ...); /*****************************************************************************/ /* 'argc/argv' are only required to support OSU, in particular CgiLibOsuInit(). */ main ( int argc, char *argv[] ) { /*********/ /* begin */ /*********/ sprintf (SoftwareID, "%s (%s)", SOFTWAREID, CgiLibEnvironmentVersion()); if (getenv ("FORMWORK$DBUG")) { Debug = true; CgiLibResponseHeader (200, "text/plain"); CgiLibEnvironmentSetDebug (Debug); } GetParameters (); /* starts off as integer kBytes and ends up as integer bytes */ if (FieldValueCapacity < 1 || FieldValueCapacity > 1024) FieldValueCapacity = 4; FieldValueCapacity = 1024 * FieldValueCapacity; CgiLibEnvironmentInit (argc, argv, false); CgiLibResponseSetCharset (CliCharsetPtr); CgiLibResponseSetSoftwareID (SoftwareID); CgiLibResponseSetErrorMessage ("Reported by FormWork"); CgiEnvironmentPtr = CgiLibEnvironmentName (); ProcessRequest (); exit (SS$_NORMAL); } /*****************************************************************************/ /* Script workhorse. */ ProcessRequest () { static struct { unsigned short buf_len; unsigned short item; void *buf_addr; void *ret_len; } JpiItems [] = { { sizeof(ScriptPid), JPI$_PID, &ScriptPid, 0 }, { 0,0,0,0 } }; int cnt, status; char *cptr, *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessRequest()\n"); /* for time-stamping file names, etc. */ sys$gettim (&BinTime); sys$numtim (&NumTime, &BinTime); /* get the script PID for unique-ifying any output file names */ status = sys$getjpiw (0, 0, 0, &JpiItems, 0, 0, 0); if (VMSnok (status)) exit (status); if (cptr = getenv ("FORMWORK$WATCH")) if (strsame (cptr, CliHtmlFileNamePtr, -1)) FormWorkWatch = true; /* put together a string trying to identify the source host */ cptr = CgiLibVar ("REMOTE_ADDR"); sptr = CgiLibVar ("REMOTE_HOST"); if (strsame (cptr, sptr, -1)) cnt = sprintf (SourceHost, "%s", cptr); else cnt = sprintf (SourceHost, "%s (%s)", sptr, cptr); cptr = CgiLibVarNull ("HTTP_X_FORWARDED_FOR"); if (cptr) cnt += sprintf (SourceHost+cnt, " (%s)", cptr); cptr = CgiLibVarNull ("HTTP_FORWARDED"); if (cptr) cnt += sprintf (SourceHost+cnt, " (%s)", cptr); if (cnt > sizeof(SourceHost)) exit (SS$_BUGCHECK); status = ReadFileIntoMemory (CliHtmlFileNamePtr, &FormPtr, &FormLength); if (VMSnok (status)) { CgiLibResponseError (FI_LI, status, ErrorReadingFile); exit (QUIET_ERROR_EXIT); } if (FormWorkWatch) { CgiLibResponseHeader (200, "text/plain"); fprintf (stdout, "%sHTML\n%s%sINPUTS\n", WATCH_DIVIDER, FormPtr, WATCH_DIVIDER); } ProcessHtmlFile (); CgiLibReadRequestBody (&BodyBufferPtr, &BodyBufferCount); if (!BodyBufferPtr) BodyBufferPtr = ""; if (Debug) fprintf (stdout, "%d |%s|\n", BodyBufferCount, BodyBufferPtr); if (FormWorkWatch) fprintf (stdout, "%sBODY\n%s\n%sFIELDS\n", WATCH_DIVIDER, BodyBufferPtr, WATCH_DIVIDER); ProcessRequestBody (); if (PrintfReportPtr) { if (FormWorkWatch) fprintf (stdout, "%sRESPONSE\n", WATCH_DIVIDER); CgiLibResponseHeader (200, "text/html"); fprintf (stdout, "<html>\n<head></head>\n<body>\n"); fprintf (stdout, ReportToClient, PrintfReportPtr); fprintf (stdout, "</body>\n</head>\n"); exit (QUIET_ERROR_EXIT); } else { if (CliCsvFileNamePtr || CliEmailAddressPtr || CliProcessedFileNamePtr || CliRawFileNamePtr) { if (FormWorkWatch) fprintf (stdout, "%sACTION\n", WATCH_DIVIDER); FormatResponse (); if (CliRawFileNamePtr) { status = WriteRawFile (); if (VMSnok (status)) { CgiLibResponseError (FI_LI, status, ErrorWritingFile); exit (QUIET_ERROR_EXIT); } } if (CliProcessedFileNamePtr) { status = WriteProcessedFile (); if (VMSnok (status)) { CgiLibResponseError (FI_LI, status, ErrorWritingFile); exit (QUIET_ERROR_EXIT); } } if (CliEmailAddressPtr) { char MailSubject [256]; if (!CliSubjectPtr) { sprintf (MailSubject, "FormWork: %s", CliHtmlFileNamePtr); CliSubjectPtr = MailSubject; } MailMessage (CliPersonalNamePtr, CliEmailAddressPtr, CliSubjectPtr, PrintfStringPtr); } /* must be done last because it reuses the FprintfString() storage */ if (CliCsvFileNamePtr) { status = WriteCsvFile (); if (VMSnok (status)) { CgiLibResponseError (FI_LI, status, ErrorWritingFile); exit (QUIET_ERROR_EXIT); } } } if (FormWorkWatch) fprintf (stdout, "%sRESPONSE\n", WATCH_DIVIDER); if (CliLocationPtr) CgiLibResponseRedirect (CliLocationPtr); else if (!CliNoResponse) { fprintf (stdout, "Status: 200\r\nContent-Type: text/html\r\n\r\n"); fprintf (stdout, "%s\n", SuccessMessage); } if (CliSymbolPtr) SetCliSymbols (); } if (FormWorkWatch) fprintf (stdout, "%sEND\n", WATCH_DIVIDER); } /*****************************************************************************/ /* Generate a human-readable version of the submitted form. This in-memory version is used to generate the processed file and for the email body. */ FormatResponse () { int idx; char *cptr, *sptr; FIELD_STRUCT *fptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "FormatResponse()\n"); /* reset the string function */ PrintfString (NULL); PrintfString ("\ Date: %02.02d %s %04.04d %02.02d:%02.02d:%02.02d\n\ Server: %s:%s\n\ Host: %s\n\ Agent: %s\n\n", NumTime[2], MonthName[NumTime[1]], NumTime[0], NumTime[3], NumTime[4], NumTime[5], CgiLibVar("SERVER_NAME"), CgiLibVar("SERVER_PORT"), SourceHost, CgiLibVar("HTTP_USER_AGENT")); for (idx = 0; idx < FieldCount; idx++) { fptr = &FieldData[idx]; if (fptr->IgnoreField) continue; for (cptr = fptr->ValuePtr; *cptr && *cptr != '\n'; cptr++); if (*cptr) { PrintfString ("%s:\n\n", fptr->DescPtr, fptr->ValuePtr); cptr = fptr->ValuePtr; while (*cptr) { sptr = cptr; while (*cptr && *cptr != '\n') cptr++; PrintfString (" %*.*s\n", cptr-sptr, cptr-sptr, sptr); if (*cptr) cptr++; } PrintfString ("\n"); } else PrintfString ("%s: %s%s", fptr->DescPtr, fptr->ValuePtr, CliDoubleSpace ? "\n\n" : "\n"); } PrintfString ("%s: %s\n", SOFTWAREID, CliHtmlFileNamePtr); if (Debug) fprintf (stdout, "PrintfStringPtr |%s|\n", PrintfStringPtr); } /*****************************************************************************/ /* Process the in-memory request body, parsing out URL-form-encoded filed names and the corresponding values checking each against the field directives previously read from the file of the generating form. If there are any discrepancies between the submitted and required daya then note each for later reporting to the client. */ ProcessRequestBody () { int idx, len, FieldNameLength, FieldValueLength, FormWorkCount; char *cptr, *sptr, *zptr, *FieldValue; char FieldName [256]; FIELD_STRUCT *fptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessRequestBody()\n"); FormWorkCount = 0; FieldValue = calloc (1, FieldValueCapacity); if (!FieldValue) exit (vaxc$errno); cptr = BodyBufferPtr; while (*cptr) { /**************/ /* field name */ /**************/ FieldName[0] = '\0'; FieldNameLength = 0; while (*cptr && *cptr != '=') { zptr = (sptr = FieldName) + sizeof(FieldName)-1; while (*cptr && *cptr != '=' && sptr < zptr) { if (isprint(*cptr)) *sptr++ = *cptr++; else cptr++; } if (sptr >= zptr) { CgiLibResponseError (FI_LI, 0, ErrorBufferOverflow); exit (QUIET_ERROR_EXIT); } *sptr = '\0'; if (Debug) fprintf (stdout, "FieldName: |%s|\n", FieldName); FieldNameLength += sptr - FieldName; } if (*cptr == '=') cptr++; /***************/ /* field value */ /***************/ FieldValue[0] = '\0'; FieldValueLength = 0; while (*cptr && *cptr != '&') { zptr = (sptr = FieldValue) + FieldValueCapacity - 1; /* absorb leading white-space */ while (*cptr && *cptr != '&' && isspace(*cptr)) cptr++; /* copy value while stripping carriage-control and non-printables */ while (*cptr && *cptr != '&' && sptr < zptr) { if (isprint(*cptr)) *sptr++ = *cptr++; else cptr++; } if (sptr >= zptr) { CgiLibResponseError (FI_LI, 0, ErrorBufferOverflow); exit (QUIET_ERROR_EXIT); } /* absorb trailing white-space */ if (sptr > FieldValue && isspace(*(sptr-1))) { sptr--; while (sptr > FieldValue && isspace(*sptr)) sptr++; if (!isspace(*sptr)) sptr++; } *sptr = '\0'; if (Debug) fprintf (stdout, "FieldValue: |%s|\n", FieldValue); CgiLibUrlDecode (FieldValue); FieldValueLength = strlen(FieldValue); } if (*cptr == '&') cptr++; /***********************/ /* find the field data */ /***********************/ if (FormWorkWatch) fprintf (stdout, "%03.03d %3.d|%s|\n %3.d|%s|\n", ++FormWorkCount, strlen(FieldName), FieldName, strlen(FieldValue), FieldValue); for (idx = 0; idx < FieldCount; idx++) if (strsame (FieldData[idx].NamePtr, FieldName, -1)) break; if (idx >= FieldCount) { if (FormWorkWatch) fprintf (stdout, "FIELD NAME NOT FOUND %d|%s|\n", strlen(FieldName), FieldName); CgiLibResponseError (FI_LI, 0, ErrorConfig); exit (QUIET_ERROR_EXIT); } /*******************/ /* store the value */ /*******************/ if (FieldData[idx].ValuePtr && FieldData[idx].ValuePtr[0]) { if (FieldValue[0]) { len = strlen(FieldData[idx].ValuePtr); FieldData[idx].ValuePtr = realloc (FieldData[idx].ValuePtr, len+1+FieldValueLength+1); FieldData[idx].ValuePtr[len] = '+'; strcpy (FieldData[idx].ValuePtr+len+1, FieldValue); } } else { FieldData[idx].ValuePtr = calloc (1, FieldValueLength+1); strcpy (FieldData[idx].ValuePtr, FieldValue); } if (FormWorkWatch) if (FieldData[idx].ValuePtr) fprintf (stdout, " %3.d|%s|\n", strlen(FieldData[idx].ValuePtr), FieldData[idx].ValuePtr); else fprintf (stdout, " %3.d|{null}|\n", 0); } /****************************/ /* check/process the fields */ /****************************/ for (idx = 0; idx < FieldCount; idx++) { fptr = &FieldData[idx]; if (Debug) fprintf (stdout, "%03d %d %d %d %d %d |%s|%s|%s|%s|\n", idx+1, fptr->EmailField, fptr->IgnoreField, fptr->MandatoryField, fptr->NumericField, fptr->MaxSize, fptr->NamePtr, fptr->FormWorkPtr, fptr->DescPtr, fptr->ValuePtr); if (fptr->MandatoryField && (!fptr->ValuePtr || !fptr->ValuePtr[0])) { PrintfReport (ReportMandatory, fptr->DescPtr); continue; } if (fptr->EmailField) { for (cptr = fptr->ValuePtr; *cptr && *cptr != '@'; cptr++); if (!*cptr || cptr == fptr->ValuePtr) PrintfReport (ReportNotEmail, fptr->DescPtr); else { cptr++; sptr = cptr; while (*cptr) cptr++; if (cptr == sptr) PrintfReport (ReportNotEmail, fptr->DescPtr); } } if (fptr->MaxSize) { if (fptr->ValuePtr && strlen(fptr->ValuePtr) > fptr->MaxSize) PrintfReport (ReportTooLarge, fptr->DescPtr, fptr->MaxSize); } if (fptr->NumericField) { for (cptr = fptr->ValuePtr; *cptr && isdigit(*cptr); cptr++); if (*cptr) PrintfReport (ReportNotNumeric, fptr->DescPtr); } if (!fptr->AsIsCarCon && fptr->ValuePtr) { /* by default carriage-control is replaced by spaces */ cptr = fptr->ValuePtr; while (*cptr && *cptr != '\r' && *cptr != '\n') cptr++; if (*cptr) { sptr = cptr; while (*cptr) { if (*(unsigned short*)cptr == '\r\n') { *sptr++ = ' '; cptr += 2; } else if (*cptr == '\r' || *cptr == '\n') { *sptr++ = ' '; cptr++; } else *sptr++ = *cptr++; } *sptr = '\0'; } } } free (FieldValue); } /*****************************************************************************/ /* Some small functions used by ProcessHtmlFile(). */ char* FindStartOfTag (char *cptr) { while (*cptr && *cptr != '<') cptr++; return (cptr); } char* FindEndOfTag (char *cptr) { while (*cptr) { while (*cptr && *cptr != '>' && *cptr != '\"') cptr++; if (!*cptr || *cptr == '>') break; cptr++; while (*cptr && *cptr != '\"') cptr++; if (*cptr) cptr++; } return (cptr); } /* find something like 'name="content"' */ char* FindElement (char *cptr, char *eptr, int elen) { while (*cptr) { while (*cptr && *cptr != '\"' && *cptr != '>' && !strsame(cptr, eptr, elen)) cptr++; if (*cptr != '\"') break; cptr++; while (*cptr && *cptr != '\"') cptr++; if (*cptr) cptr++; } return (cptr); } /* get the content from something like 'name="content"' */ char* GetElement (char *cptr, char *bptr, int blen) { char *sptr, *zptr; zptr = (sptr = bptr) + blen-1; if (*cptr == '\"') { cptr++; while (*cptr && *cptr != '\"' && sptr < zptr) *sptr++ = *cptr++; if (*cptr) cptr++; } else while (*cptr && *cptr != '>' && !isspace(*cptr) && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; return (cptr); } /*****************************************************************************/ /* Read the contents of the /HTML= specified HTML file containing the form and the formwork="" directives into memory. Parse that file populating the 'FormData' array with the form data. While processing check the directives for anomalies and report as necessary. */ ProcessHtmlFile () { int idx, status, FormWorkCount, FormWorkDataLength, InputDescLength, InputLiteralLength, InputNameLength; char *cptr, *sptr, *zptr; char FormWorkData [256], FormName [256], InputDesc [256], InputLiteral [256], InputName [256], InputTag [1024], InputType [256], Scratch [256]; FIELD_STRUCT *fptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessHtmlFile()\n"); FormWorkCount = 0; cptr = FormPtr; while (*cptr) { /********************************/ /* find the (next) <form..> tag */ /********************************/ cptr = FindStartOfTag (cptr); if (!*cptr) break; if (!strsame (cptr, "<form ", 6)) { cptr = FindEndOfTag (cptr); continue; } /*************************/ /* look for 'input' tags */ /*************************/ while (*cptr) { while (*cptr) { cptr = FindStartOfTag (cptr); if (!*cptr) break; /* if end of this form */ if (strsame (cptr, "</form", 6)) break; /* if this is a tag we're looking for */ if (strsame (cptr, "<input ", 7)) break; if (strsame (cptr, "<textarea ", 10)) break; if (strsame (cptr, "<select ", 8)) break; cptr = FindEndOfTag (cptr); } if (!*cptr) break; if (strsame (cptr, "</form", 6)) break; /***************/ /* 'input' tag */ /***************/ if (FormWorkWatch) { sptr = FindEndOfTag (cptr); memset (InputTag, 0, sizeof(InputTag)); memcpy (InputTag, cptr, sptr-cptr+1 > sizeof(InputTag)-1 ? sizeof(InputTag)-1 : sptr-cptr+1); for (sptr = InputTag; *sptr; sptr++) if (*sptr == '\r' || *sptr == '\n') *sptr = ' '; } InputName[0] = FormWorkData[0] = '\0'; sptr = cptr; if (strsame (cptr, "<input ", 7)) { /* <textarea> and <select> tasg do not use type=".." */ cptr = FindElement (cptr, "type=", 5); /* if there is no 'type=' element! */ if (!*cptr || *cptr == '>') { cptr = FindEndOfTag (sptr); if (*cptr) cptr++; *cptr = '\0'; if (FormWorkWatch) fprintf (stdout, "NO \'type=\' %d|%s|\n", strlen(sptr), sptr); CgiLibResponseError (FI_LI, 0, ErrorConfig); exit (QUIET_ERROR_EXIT); } cptr += 5; cptr = GetElement (cptr, InputType, sizeof(InputType)); if (Debug) fprintf (stdout, "InputType |%s|\n", InputType); if (strsame (InputType, "reset", -1)) { cptr = FindEndOfTag (sptr); continue; } } cptr = FindElement (sptr, "name=", 5); /* if there is no 'name=' element! */ if (!*cptr || *cptr == '>') { cptr = FindEndOfTag (sptr); /* only worry about submit buttons with name=".." elements */ if (strsame (InputType, "submit", -1)) continue; if (*cptr) cptr++; *cptr = '\0'; if (FormWorkWatch) fprintf (stdout, "NO \'name=\' %d|%s|\n", strlen(sptr), sptr); CgiLibResponseError (FI_LI, 0, ErrorConfig); exit (QUIET_ERROR_EXIT); } cptr += 5; cptr = GetElement (cptr, InputName, sizeof(InputName)); if (Debug) fprintf (stdout, "InputName |%s|\n", InputName); InputNameLength = strlen(InputName); cptr = FindElement (sptr, "formwork=", 9); if (!*cptr || *cptr == '>') { /* there is no 'formwork=' element! */ FormWorkData[0] = '\0'; FormWorkDataLength = 0; } else { cptr += 9; cptr = GetElement (cptr, FormWorkData, sizeof(FormWorkData)); if (Debug) fprintf (stdout, "FormWorkData |%s|\n", FormWorkData); FormWorkDataLength = strlen(FormWorkData); } /********************************************/ /* process tag name and (any) formwork data */ /********************************************/ if (FormWorkWatch) fprintf (stdout, "%03.03d %3.d|%s|\n %3.d|%s|\n", ++FormWorkCount, strlen(InputTag), InputTag, strlen(FormWorkData), FormWorkData); for (idx = 0; idx < FieldCount; idx++) if (strsame (FieldData[idx].NamePtr, InputName, -1)) break; fptr = &FieldData[idx]; if (idx >= FieldCount) { /* new input field */ if (++FieldCount >= MAX_FIELDS) { if (FormWorkWatch) fprintf (stdout, "MAX_FIELDS %d EXCEEDED\n", MAX_FIELDS); CgiLibResponseError (FI_LI, 0, ErrorConfig); exit (QUIET_ERROR_EXIT); } fptr->NamePtr = calloc (1, InputNameLength+1); strcpy (fptr->NamePtr, InputName); } if (FormWorkData[0]) { char *cptr, *sptr, *zptr; fptr->FormWorkPtr = calloc (1, FormWorkDataLength+1); strcpy (fptr->FormWorkPtr, FormWorkData); cptr = sptr = FormWorkData; while (*cptr) { if (Debug) fprintf (stdout, "cptr |%s|\n", cptr); while (*cptr && !isalpha(*cptr)) cptr++; switch (*(unsigned short*)cptr) { case 'as' : case 'AS' : cptr += 2; fptr->AsIsCarCon = true; break; case 'cg' : case 'CG' : sptr = cptr; cptr += 2; if (*cptr != '{') { if (FormWorkWatch) fprintf (stdout, "NO CGI \'{\' |%s|\n", sptr); CgiLibResponseError (FI_LI, 0, ErrorConfig); exit (QUIET_ERROR_EXIT); } cptr++; zptr = (sptr = Scratch) + sizeof(Scratch)-1; while (*cptr && *cptr != '}' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; if (*cptr == '}') cptr++; sptr = CgiLibVarNull (Scratch); if (!sptr) sptr = "{none}"; fptr->ValuePtr = calloc (1, strlen(sptr)+1); strcpy (fptr->ValuePtr, sptr); break; case 'de' : case 'DE' : sptr = cptr; cptr += 2; if (*cptr != '{') { if (FormWorkWatch) fprintf (stdout, "NO DE \'{\' |%s|\n", sptr); CgiLibResponseError (FI_LI, 0, ErrorConfig); exit (QUIET_ERROR_EXIT); } cptr++; zptr = (sptr = InputDesc) + sizeof(InputDesc)-1; while (*cptr && *cptr != '}' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; if (*cptr == '}') cptr++; InputDescLength = sptr - InputDesc; fptr->DescPtr = calloc (1, InputDescLength+1); strcpy (fptr->DescPtr, InputDesc); break; case 'em' : case 'EM' : cptr += 2; fptr->EmailField = true; break; case 'ig' : case 'IG' : cptr += 2; fptr->IgnoreField = true; break; case 'li' : case 'LI' : sptr = cptr; cptr += 2; if (*cptr != '{') { if (FormWorkWatch) fprintf (stdout, "NO LI \'{\' |%s|\n", sptr); CgiLibResponseError (FI_LI, 0, ErrorConfig); exit (QUIET_ERROR_EXIT); } cptr++; zptr = (sptr = InputLiteral) + sizeof(InputLiteral)-1; while (*cptr && *cptr != '}' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; if (*cptr == '}') cptr++; InputLiteralLength = sptr - InputLiteral; fptr->ValuePtr = calloc (1, InputLiteralLength+1); strcpy (fptr->ValuePtr, InputLiteral); break; case 'ma' : case 'MA' : cptr += 2; fptr->MandatoryField = true; break; case 'nu' : case 'NU' : cptr += 2; fptr->NumericField = true; break; case 'op' : case 'OP' : /* just for documentation purposes really */ cptr += 2; break; case 'si' : case 'SI' : sptr = cptr; cptr += 2; fptr->MaxSize = atoi(cptr); if (!isdigit(*cptr) || !fptr->MaxSize) { if (FormWorkWatch) fprintf (stdout, "NO SIZE |%s|\n", sptr); CgiLibResponseError (FI_LI, 0, ErrorConfig); exit (QUIET_ERROR_EXIT); } while (*cptr && isdigit(*cptr)) cptr++; break; case 'wa' : case 'WA' : cptr += 2; if (!FormWorkWatch) { CgiLibResponseHeader (200, "text/plain"); fprintf (stdout, "%sHTML\n%s%sINPUTS\n", WATCH_DIVIDER, FormPtr, WATCH_DIVIDER); FormWorkWatch = true; } break; default : if (FormWorkWatch) fprintf (stdout, "UNKNOWN FORMWORK |%s|\n", cptr); CgiLibResponseError (FI_LI, 0, ErrorConfig); exit (QUIET_ERROR_EXIT); } } if (Debug) fprintf (stdout, "%03d %d %d %d %d %d |%s|%s|%s|%s|\n", FieldCount, fptr->EmailField, fptr->IgnoreField, fptr->MandatoryField, fptr->NumericField, fptr->MaxSize, fptr->NamePtr, fptr->FormWorkPtr, fptr->DescPtr, fptr->ValuePtr); } } } /***********************************/ /* check all input have essentials */ /***********************************/ for (idx = 0; idx < FieldCount; idx++) { fptr = &FieldData[idx]; if (!fptr->FormWorkPtr) { if (FormWorkWatch) fprintf (stdout, "NO \'formwork=\' %d|%s|\n", strlen(fptr->NamePtr), fptr->NamePtr); CgiLibResponseError (FI_LI, 0, ErrorConfig); exit (QUIET_ERROR_EXIT); } if (fptr->IgnoreField) continue; if (!fptr->DescPtr) { if (FormWorkWatch) fprintf (stdout, "NO DE{..} %d|%s|\n", strlen(fptr->NamePtr), fptr->NamePtr); CgiLibResponseError (FI_LI, 0, ErrorConfig); exit (QUIET_ERROR_EXIT); } } } /*****************************************************************************/ /* Write the URL-form-encoded (not decoded) request body into a time-stamped file. Prepend some relevant CGI source host information in URL-form-encoded format. */ WriteRawFile () { int cnt, status; char FileName [256]; FILE *fp; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "WriteRawFile()\n"); cnt = sprintf (FileName, "%s%04.04d%02.02d%02.02d%02.02d%02.02d%02.02d%02.02d_%08.08X_RAW.TXT", CliRawFileNamePtr, NumTime[0], NumTime[1], NumTime[2], NumTime[3], NumTime[4], NumTime[5], NumTime[6], ScriptPid); if (FormWorkWatch) fprintf (stdout, "WRITE RAW %d|%s|\n", cnt, FileName); fp = fopen (FileName, "w", "shr=nil"); if (!fp) return (vaxc$errno); cnt = fprintf (fp, "REMOTE_ADDR=%s&REMOTE_HOST=%s&HTTP_FORWARDED=%s&HTTP_X_FORWARDED_FOR=%s&%s", CgiLibVar("REMOTE_ADDR"), CgiLibVar("REMOTE_HOST"), CgiLibVar("HTTP_FORWARDED"), CgiLibVar("HTTP_X_FORWARDED_FOR"), BodyBufferPtr); if (cnt) status = SS$_NORMAL; else status = vaxc$errno; fclose (fp); if (CliSymbolPtr) { char SymbolName [256]; char *cptr, *sptr, *zptr; zptr = (sptr = SymbolName) + sizeof(SymbolName)-1; /* prefix the symbol name */ for (cptr = CliSymbolPtr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "_FILENAME__RAW"; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; SetGlobalSymbol (SymbolName, FileName, 0); } return (status); } /*****************************************************************************/ /* Write the in-memory, human-readable submitted form into a time-stamped file. */ WriteProcessedFile () { int cnt, status; char FileName [256]; FILE *fp; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "WriteProcessedFile()\n"); cnt = sprintf (FileName, "%s%04.04d%02.02d%02.02d%02.02d%02.02d%02.02d%02.02d_%08.08X_PRO.TXT", CliProcessedFileNamePtr, NumTime[0], NumTime[1], NumTime[2], NumTime[3], NumTime[4], NumTime[5], NumTime[6], ScriptPid); if (FormWorkWatch) fprintf (stdout, "WRITE PROCESSED %d|%s|\n", cnt, FileName); fp = fopen (FileName, "w", "shr=nil"); if (!fp) return (vaxc$errno); cnt = fprintf (fp, "%s", PrintfStringPtr); if (cnt) status = SS$_NORMAL; else status = vaxc$errno; fclose (fp); if (CliSymbolPtr) { char SymbolName [256]; char *cptr, *sptr, *zptr; zptr = (sptr = SymbolName) + sizeof(SymbolName)-1; /* prefix the symbol name */ for (cptr = CliSymbolPtr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "_FILENAME__PRO"; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; SetGlobalSymbol (SymbolName, FileName, 0); } return (status); } /*****************************************************************************/ /* Write the Comma-Separated Value (even if separated by some other character) entry in the specified file. If it's the first entry when write a leading header record containing the column descriptions. */ WriteCsvFile () { int cnt, idx, status; char *cptr; FIELD_STRUCT *fptr; FILE *fp; stat_t FstatBuffer; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "WriteCsvFile()\n"); /* reset the string function */ PrintfString (NULL); if (FormWorkWatch) fprintf (stdout, "WRITE CSV %d|%s|\n", strlen(CliCsvFileNamePtr), CliCsvFileNamePtr); for (cnt = 0; cnt < 10; cnt++) { fp = fopen (CliCsvFileNamePtr, "a", "shr=put"); if (fp) break; sleep (1); } if (!fp) return (vaxc$errno); if (fstat (fileno(fp), &FstatBuffer) < 0) { status = vaxc$errno; if (Debug) fprintf (stdout, "fstat() %%X%08.08X\n", status); fclose (fp); return (status); } if (!FstatBuffer.st_size) { /* first entry, provide headings */ PrintfString ("Date%cHost%c", *CliSeparatorPtr, *CliSeparatorPtr); cnt = 0; for (idx = 0; idx < FieldCount; idx++) { fptr = &FieldData[idx]; if (fptr->IgnoreField) continue; if (cnt++) PrintfString ("%c%s", *CliSeparatorPtr, fptr->DescPtr); else PrintfString ("%s", fptr->DescPtr); } PrintfString ("\n"); } PrintfString ("%02.02d %s %04.04d %02.02d:%02.02d:%02.02d%c%s%c", NumTime[2], MonthName[NumTime[1]], NumTime[0], NumTime[3], NumTime[4], NumTime[5], *CliSeparatorPtr, SourceHost, *CliSeparatorPtr); cnt = 0; for (idx = 0; idx < FieldCount; idx++) { fptr = &FieldData[idx]; if (fptr->IgnoreField) continue; /* ensure any embedded carriage control or separator become spaces */ for (cptr = fptr->ValuePtr; *cptr; cptr++) if (*cptr == '\r' || *cptr == '\n' || *cptr == *CliSeparatorPtr) *cptr = ' '; if (cnt++) PrintfString ("%c%s", *CliSeparatorPtr, fptr->ValuePtr); else PrintfString ("%s", fptr->ValuePtr); } PrintfString ("\n"); if (Debug) fprintf (stdout, "PrintfStringPtr |%s|\n", PrintfStringPtr); cnt = fprintf (fp, "%s", PrintfStringPtr); if (cnt) status = SS$_NORMAL; else status = vaxc$errno; fclose (fp); return (status); } /*****************************************************************************/ /* */ SetCliSymbols () { int idx; char *cptr, *sptr, *zptr; char SymbolName [256]; FIELD_STRUCT *fptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SetCliSymbols()\n"); for (idx = 0; idx < FieldCount; idx++) { fptr = &FieldData[idx]; zptr = (sptr = SymbolName) + sizeof(SymbolName)-1; /* prefix the symbol name */ for (cptr = CliSymbolPtr; *cptr && sptr < zptr; *sptr++ = *cptr++); /* convert any unacceptable characters in the name into underscores */ for (cptr = fptr->NamePtr; *cptr && sptr < zptr; cptr++) if (isalpha(*cptr)) *sptr++ = *cptr; else *sptr++ = '_'; *sptr = '\0'; SetGlobalSymbol (SymbolName, fptr->ValuePtr, 0); } } /*****************************************************************************/ /* Read the request body (if POST) or the request's query string (if GET) into a single array of char (doesn't matter whether it's text or binary). */ ReadRequestDataIntoMemory ( char **BufferPtrPtr, int *DataSizePtr ) { static int BufferChunk = 1024; static int BufferCount; static char *BufferPtr; int BufferSize, ReadCount; char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ReadRequestDataIntoMemory()\n"); /* if the body has already been read then just return */ if (BufferPtr != NULL) { if (BufferPtrPtr != NULL) *BufferPtrPtr = BufferPtr; if (DataSizePtr != NULL) *DataSizePtr = BufferCount; return; } CgiRequestMethodPtr = CgiLibVar("WWW_REQUEST_METHOD"); if (strsame (CgiRequestMethodPtr, "GET", -1)) { BufferPtr = CgiLibVar("WWW_QUERY_STRING"); BufferCount = strlen (BufferPtr); if (BufferPtrPtr != NULL) *BufferPtrPtr = BufferPtr; if (DataSizePtr != NULL) *DataSizePtr = BufferCount; return; } else if (strsame (CgiRequestMethodPtr, "POST", -1)) { CgiContentTypePtr = CgiLibVar("WWW_CONTENT_TYPE"); if (!strsame (CgiContentTypePtr, FORM_URLENCODED, -1)) { CgiLibResponseError (FI_LI, 0, ErrorNotUrlEncoded); exit (QUIET_ERROR_EXIT); } } else { CgiLibResponseError (FI_LI, 0, ErrorHttpMethod); exit (QUIET_ERROR_EXIT); } CgiLibReadRequestBody (&BufferPtr, &BufferCount); if (BufferPtrPtr != NULL) *BufferPtrPtr = BufferPtr; if (DataSizePtr != NULL) *DataSizePtr = BufferCount; } /****************************************************************************/ /* Read the file contents specified by 'FileName' into memory, set the pointer at 'FileTextPtr' to the contents and the file size at 'FileSizePtr'. Returns a VMS status value that should be checked. */ int ReadFileIntoMemory ( char *Source, char **FileTextPtr, int *FileSizePtr ) { static int BytesRemaining; int status, Bytes, BufferCount, Length; char *BufferPtr, *LinePtr; FILE *fp; stat_t FstatBuffer; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ReadFileIntoMemory() |%s|\n", Source); if ((fp = fopen (Source, "r", "shr=get", "shr=put")) == NULL) { status = vaxc$errno; if (Debug) fprintf (stdout, "fopen() %%X%08.08X\n", status); return (status); } if (fstat (fileno(fp), &FstatBuffer) < 0) { status = vaxc$errno; if (Debug) fprintf (stdout, "fstat() %%X%08.08X\n", status); fclose (fp); return (status); } Bytes = FstatBuffer.st_size; if (Debug) fprintf (stdout, "%d bytes\n", Bytes); /* a little margin for error ;^) */ Bytes += 32; BufferPtr = calloc (Bytes, 1); if (!BufferPtr) { status = vaxc$errno; CgiLibResponseError (FI_LI, status, "calloc()"); exit (status); } BytesRemaining = Bytes; LinePtr = BufferPtr; BufferCount = 0; while (fgets (LinePtr, BytesRemaining, fp) != NULL) { /** if (Debug) fprintf (stdout, "|%s|\n", LinePtr); **/ Length = strlen(LinePtr); LinePtr += Length; BufferCount += Length; BytesRemaining -= Length; } fclose (fp); if (Debug) fprintf (stdout, "%d |%s|\n", BufferCount, BufferPtr); if (FileTextPtr != NULL) *FileTextPtr = BufferPtr; if (FileSizePtr != NULL) *FileSizePtr = BufferCount; return (SS$_NORMAL); } /*****************************************************************************/ /* Use the VMS callable mail interface to create and send a VMS mail message. 'To' can be a list of comma-separated addresses. 'Subject' is a null-terminated string. 'Body' is a null-terminated string of '\n'-separated lines of plain text. Just truncates anything longer than 255 characters (body excluded, body records included)! */ int MailMessage ( char *PersonalName, char *To, char *Subject, char *Body ) { int status; unsigned long SendContext = 0; char *cptr, *sptr; struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } BodyPartItem [] = { { 0, MAIL$_SEND_RECORD, 0, 0 }, { 0, MAIL$_NOSIGNAL, 0, 0 }, {0,0,0,0} }, PersonalNameItem [] = { { 0, MAIL$_SEND_PERS_NAME, PersonalName, 0 }, { 0, MAIL$_NOSIGNAL, 0, 0 }, {0,0,0,0} }, SendUserNameItem [] = { { 0, MAIL$_SEND_USERNAME, 0, 0 }, { 0, MAIL$_NOSIGNAL, 0, 0 }, {0,0,0,0} }, SubjectItem [] = { { 0, MAIL$_SEND_SUBJECT, Subject, 0 }, { 0, MAIL$_NOSIGNAL, 0, 0 }, {0,0,0,0} }, NoSignalItem [] = { { 0, MAIL$_NOSIGNAL, 0, 0 }, {0,0,0,0} }, NullItem = {0,0,0,0}; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MailMessage() |%s|\n|%s|\n|%s|\n", PersonalName, To, Subject); if (PersonalName != NULL && PersonalName[0]) { PersonalNameItem[0].buf_len = strlen(PersonalName); if (PersonalNameItem[0].buf_len > 255) PersonalNameItem[0].buf_len = 255; status = mail$send_begin (&SendContext, &PersonalNameItem, &NullItem); } else status = mail$send_begin (&SendContext, &NoSignalItem, &NullItem); if (VMSnok (status)) { CgiLibResponseError (FI_LI, status, "beginning message"); exit (QUIET_ERROR_EXIT); } /* a single, or multiple comma-separated addresses */ cptr = To; while (*cptr) { sptr = cptr; while (*cptr && *cptr != ',') cptr++; if (*cptr) *cptr++ = '\0'; SendUserNameItem[0].buf_addr = sptr; SendUserNameItem[0].buf_len = strlen(sptr); if (SendUserNameItem[0].buf_len > 255) SendUserNameItem[0].buf_len = 255; if (Debug) fprintf (stdout, "address |%s|\n", (char*)SendUserNameItem[0].buf_addr); status = mail$send_add_address (&SendContext, &SendUserNameItem, &NullItem); if (VMSnok (status)) { CgiLibResponseError (FI_LI, status, sptr); exit (QUIET_ERROR_EXIT); } } SubjectItem[0].buf_len = strlen(Subject); if (SubjectItem[0].buf_len > 255) SubjectItem[0].buf_len = 255; status = mail$send_add_attribute (&SendContext, &SubjectItem, &NullItem); if (VMSnok (status)) { CgiLibResponseError (FI_LI, status, "adding subject"); exit (QUIET_ERROR_EXIT); } cptr = Body; while (*cptr) { BodyPartItem[0].buf_addr = cptr; while (*cptr && *cptr != '\n') cptr++; BodyPartItem[0].buf_len = cptr - (char*)BodyPartItem[0].buf_addr; if (BodyPartItem[0].buf_len > 255) BodyPartItem[0].buf_len = 255; if (*cptr) cptr++; status = mail$send_add_bodypart (&SendContext, &BodyPartItem, &NullItem); if (VMSnok (status)) { CgiLibResponseError (FI_LI, status, "adding body"); exit (QUIET_ERROR_EXIT); } } status = mail$send_message (&SendContext, &NoSignalItem, &NoSignalItem); if (VMSnok (status)) { CgiLibResponseError (FI_LI, status, "sending message"); exit (QUIET_ERROR_EXIT); } mail$send_end (&SendContext, &NullItem, &NullItem); return (SS$_NORMAL); } /****************************************************************************/ /* Create a dynamic null-terminated string using 'printf' format strings. */ void PrintfString ( char *FormatString, ... ) { int argcnt, BufferLength; char *cptr; char Buffer [4096]; va_list argptr; /*********/ /* begin */ /*********/ va_count (argcnt); if (Debug) fprintf (stdout, "PrintfString() %d |%s|\n", argcnt, FormatString); if (!FormatString) { if (PrintfStringPtr) free (PrintfStringPtr); PrintfStringPtr = NULL, PrintfStringLength = 0; return; } va_start (argptr, FormatString); BufferLength = vsprintf (Buffer, FormatString, argptr); if (BufferLength > sizeof(Buffer)-1) _exit (SS$_BUGCHECK); va_end (argptr); PrintfStringPtr = realloc (PrintfStringPtr, PrintfStringLength+BufferLength+16); if (!PrintfStringPtr) { CgiLibResponseError (FI_LI, vaxc$errno, ErrorMemory); exit (QUIET_ERROR_EXIT); } strcpy (PrintfStringPtr+PrintfStringLength, Buffer); PrintfStringLength += BufferLength; PrintfStringPtr[PrintfStringLength] = '\0'; } /****************************************************************************/ /* Create a dynamic null-terminated string using 'printf' format strings in HTML list entry format of each anomaly reported via this function. */ void PrintfReport ( char *FormatString, ... ) { int argcnt, BufferLength; char *cptr; char Buffer [1024]; va_list argptr; /*********/ /* begin */ /*********/ va_count (argcnt); if (Debug) fprintf (stdout, "PrintfReport() %d |%s|\n", argcnt, FormatString); va_start (argptr, FormatString); BufferLength = vsprintf (Buffer, FormatString, argptr); if (BufferLength > sizeof(Buffer)-1) _exit (676); va_end (argptr); PrintfReportPtr = realloc (PrintfReportPtr, PrintfReportLength+BufferLength+16); if (!PrintfReportPtr) { CgiLibResponseError (FI_LI, vaxc$errno, ErrorMemory); exit (QUIET_ERROR_EXIT); } strcpy (PrintfReportPtr+PrintfReportLength, "<li>"); PrintfReportLength += 4; strcpy (PrintfReportPtr+PrintfReportLength, Buffer); PrintfReportLength += BufferLength; PrintfReportPtr[PrintfReportLength++] = '\n'; PrintfReportPtr[PrintfReportLength] = '\0'; } /*****************************************************************************/ /* Get "command-line" parameters, whether from the command-line or from a configuration symbol or logical containing the equivalent. OSU scripts have the 'method', 'url' and 'protocol' supplied as P1, P2, P3 (these being detected and used by CGILIB), and are of no interest to this function. */ GetParameters () { static char CommandLine [1024]; static unsigned long Flags = 0; int status, SkipParameters; unsigned short Length; char ch; char *aptr, *cptr, *clptr, *sptr; $DESCRIPTOR (CommandLineDsc, CommandLine); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GetParameters()\n"); if ((clptr = getenv ("FORMWORK$PARAM")) == NULL) { /* get the entire command line following the verb */ if (VMSnok (status = lib$get_foreign (&CommandLineDsc, 0, &Length, &Flags))) exit (status); (clptr = CommandLine)[Length] = '\0'; } /* if OSU environment then skip P1, P2, P3 */ if (getenv ("WWWEXEC_RUNDOWN_STRING") != NULL) SkipParameters = 3; else SkipParameters = 0; aptr = NULL; ch = *clptr; for (;;) { if (aptr != NULL && *aptr == '/') *aptr = '\0'; if (!ch) break; *clptr = ch; if (Debug) fprintf (stdout, "clptr |%s|\n", clptr); while (*clptr && isspace(*clptr)) *clptr++ = '\0'; aptr = clptr; if (*clptr == '/') clptr++; while (*clptr && !isspace (*clptr) && *clptr != '/') { if (*clptr != '\"') { clptr++; continue; } cptr = clptr; clptr++; while (*clptr) { if (*clptr == '\"') if (*(clptr+1) == '\"') clptr++; else break; *cptr++ = *clptr++; } *cptr = '\0'; if (*clptr) clptr++; } ch = *clptr; if (*clptr) *clptr = '\0'; if (Debug) fprintf (stdout, "aptr |%s|\n", aptr); if (!*aptr) continue; if (SkipParameters) { SkipParameters--; continue; } if (strsame (aptr, "/CAPACITY=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; FieldValueCapacity = atoi(cptr); continue; } if (strsame (aptr, "/CHARSET=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliCharsetPtr = cptr; continue; } if (strsame (aptr, "/CSV=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliCsvFileNamePtr = cptr; continue; } if (strsame (aptr, "/DBUG", -1)) { Debug = true; continue; } if (strsame (aptr, "/DOUBLESPACE", 4)) { CliDoubleSpace = true; continue; } if (strsame (aptr, "/EMAIL=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliEmailAddressPtr = cptr; continue; } if (strsame (aptr, "/HTML=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliHtmlFileNamePtr = cptr; continue; } if (strsame (aptr, "/LOCATION=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliLocationPtr = cptr; continue; } if (strsame (aptr, "/NORESPONSE", 6)) { CliNoResponse = true; continue; } if (strsame (aptr, "/PERSONAL_NAME=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliPersonalNamePtr = cptr; continue; } if (strsame (aptr, "/PROCESSED=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliProcessedFileNamePtr = cptr; continue; } if (strsame (aptr, "/RAW=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliRawFileNamePtr = cptr; continue; } if (strsame (aptr, "/SEPARATOR=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliSeparatorPtr = cptr; continue; } if (strsame (aptr, "/SOFTWAREID", 4) || strsame (aptr, "/VERSION", 4)) { fprintf (stdout, "%%%s-I-SOFTWAREID, %s\n%s\n", Utility, SoftwareID, CopyrightInfo); exit (SS$_NORMAL); } if (strsame (aptr, "/SUBJECT=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliSubjectPtr = cptr; continue; } if (strsame (aptr, "/SYMBOLS=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; if (*cptr) CliSymbolPtr = cptr; continue; } if (strsame (aptr, "/WATCH", 4)) { FormWorkWatch = true; continue; } if (*aptr == '/') { fprintf (stdout, "%%%s-E-IVQUAL, unrecognized qualifier\n \\%s\\\n", Utility, aptr+1); exit (QUIET_ERROR_EXIT); } fprintf (stdout, "%%%s-E-MAXPARM, too many parameters\n \\%s\\\n", Utility, aptr); exit (QUIET_ERROR_EXIT); } } /****************************************************************************/ /* Assign a global symbol. If the string pointer is null the numeric value is used. Symbol lengths are adjusted according to the maximum allowed for the version of the operating system. */ SetGlobalSymbol ( char *Name, char *String, int Value ) { static int SymbolType = LIB$K_CLI_GLOBAL_SYM; static char ValueString [32]; static $DESCRIPTOR (NameDsc, ""); static $DESCRIPTOR (ValueDsc, ""); static $DESCRIPTOR (ValueFaoDsc, "!UL"); static $DESCRIPTOR (ValueStringDsc, ValueString); int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SetGlobalSymbol() |%s|%s| %d\n", Name, String, Value); NameDsc.dsc$a_pointer = Name; NameDsc.dsc$w_length = strlen(Name); if (!String) { ValueDsc.dsc$a_pointer = ValueString; sys$fao (&ValueFaoDsc, &ValueDsc.dsc$w_length, &ValueStringDsc, Value); ValueString[ValueDsc.dsc$w_length] = '\0'; } else { ValueDsc.dsc$a_pointer = String; ValueDsc.dsc$w_length = strlen(String); if (ValueDsc.dsc$w_length > 255 && VmsVersion() < 700) ValueDsc.dsc$w_length = 255; else if (ValueDsc.dsc$w_length > 1023 && VmsVersion() < 732) ValueDsc.dsc$w_length = 1023; else if (ValueDsc.dsc$w_length > 8191) ValueDsc.dsc$w_length = 8191; } if (VMSnok (status = lib$set_symbol (&NameDsc, &ValueDsc, &SymbolType))) exit (status); } /****************************************************************************/ /* Return an integer reflecting the major, minor and other VMS version number. For example, return 600 for "V6.0", 730 for "V7.3" and 732 for "V7.3-2". */ int VmsVersion () { static int VersionInteger; static char SyiVersion [8+1]; static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } SyiItems [] = { { 8, SYI$_VERSION, &SyiVersion, 0 }, { 0,0,0,0 } }; int status; char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "VmsVersion() %d\n", VersionInteger); if (VersionInteger) return (VersionInteger); if (VMSnok (status = sys$getsyiw (0, 0, 0, &SyiItems, 0, 0, 0))) exit (status); if (cptr = getenv("WASD_VMS_VERSION")) strncpy (SyiVersion, cptr, sizeof(SyiVersion)); SyiVersion[sizeof(SyiVersion)-1] = '\0'; if (Debug) fprintf (stdout, "SyiVersion |%s|\n", SyiVersion); if (SyiVersion[0] == 'V' && isdigit(SyiVersion[1]) && SyiVersion[2] == '.' && isdigit(SyiVersion[3])) { /* e.g. "V7.3" */ VersionInteger = ((SyiVersion[1]-48) * 100) + ((SyiVersion[3]-48) * 10); /* if something like "V7.3-2" */ if (SyiVersion[4] == '-') VersionInteger += SyiVersion[5]-48; } else { fprintf (stdout, "%%%s-E-VMS, cannot understand VMS version string \"%s\"\n\ -%s-I-KLUDGE, continue by using WASD_VMS_VERSION environment variable\n", Utility, SyiVersion, Utility); exit (SS$_BUGCHECK); } if (Debug) fprintf (stdout, "%d\n", VersionInteger); return (VersionInteger); } /****************************************************************************/ /* Does a case-insensitive, character-by-character string compare and returns true if two strings are the same, or false if not. If a maximum number of characters are specified only those will be compared, if the entire strings should be compared then specify the number of characters as 0. */ boolean strsame ( char *sptr1, char *sptr2, int count ) { /*********/ /* begin */ /*********/ /** if (Debug) fprintf (stdout, "strsame() |%s|%s| %d\n", sptr1, sptr2, count); **/ while (*sptr1 && *sptr2) { if (toupper (*sptr1++) != toupper (*sptr2++)) return (false); if (count) if (!--count) return (true); } if (*sptr1 || *sptr2) return (false); else return (true); } /*****************************************************************************/