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
|
# This is a shell archive. Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file". Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
# cpp1.c
# cpp3.c
# cpp4.c
#
echo x - cpp1.c
sed 's/^X//' >cpp1.c << 'END-of-cpp1.c'
X/*
X * CPP main program.
X *
X * Edit history
X * 21-May-84 MM "Field test" release
X * 23-May-84 MM Some minor hacks.
X * 30-May-84 ARF Didn't get enough memory for __DATE__
X * Added code to read stdin if no input
X * files are provided.
X * 29-Jun-84 MM Added ARF's suggestions, Unixifying cpp.
X * 11-Jul-84 MM "Official" first release (that's what I thought!)
X * 22-Jul-84 MM/ARF/SCK Fixed line number bugs, added cpp recognition
X * of #line, fixed problems with #include.
X * 23-Jul-84 MM More (minor) include hacking, some documentation.
X * Also, redid cpp's #include files
X * 25-Jul-84 MM #line filename isn't used for #include searchlist
X * #line format is <number> <optional name>
X * 25-Jul-84 ARF/MM Various bugs, mostly serious. Removed homemade doprint
X * 01-Aug-84 MM Fixed recursion bug, remove extra newlines and
X * leading whitespace from cpp output.
X * 02-Aug-84 MM Hacked (i.e. optimized) out blank lines and unneeded
X * whitespace in general. Cleaned up unget()'s.
X * 03-Aug-84 Keie Several bug fixes from Ed Keizer, Vrije Universitet.
X * -- corrected arg. count in -D and pre-defined
X * macros. Also, allow \n inside macro actual parameter
X * lists.
X * 06-Aug-84 MM If debugging, dump the preset vector at startup.
X * 12-Aug-84 MM/SCK Some small changes from Sam Kendall
X * 15-Aug-84 Keie/MM cerror, cwarn, etc. take a single string arg.
X * cierror, etc. take a single int. arg.
X * changed LINE_PREFIX slightly so it can be
X * changed in the makefile.
X * 31-Aug-84 MM USENET net.sources release.
X * 7-Sep-84 SCH/ado Lint complaints
X * 10-Sep-84 Keie Char's can't be signed in some implementations
X * 11-Sep-84 ado Added -C flag, pathological line number fix
X * 13-Sep-84 ado Added -E flag (does nothing) and "-" file for stdin.
X * 14-Sep-84 MM Allow # 123 as a synonym for #line 123
X * 19-Sep-84 MM scanid always reads to token, make sure #line is
X * written to a new line, even if -C switch given.
X * Also, cpp - - reads stdin, writes stdout.
X * 03-Oct-84 ado/MM Several changes to line counting and keepcomments
X * stuff. Also a rewritten control() hasher -- much
X * simpler and no less "perfect". Note also changes
X * in cpp3.c to fix numeric scanning.
X * 04-Oct-84 MM Added recognition of macro formal parameters if
X * they are the only thing in a string, per the
X * draft standard.
X * 08-Oct-84 MM One more attack on scannumber
X * 15-Oct-84 MM/ado Added -N to disable predefined symbols. Fixed
X * linecount if COMMENT_INVISIBLE enabled.
X * 22-Oct-84 MM Don't evaluate the #if/#ifdef argument if
X * compilation is supressed. This prevents
X * unnecessary error messages in sequences such as
X * #ifdef FOO -- undefined
X * #if FOO == 10 -- shouldn't print warning
X * 25-Oct-84 MM Fixed bug in false ifdef supression. On vms,
X * #include <foo> should open foo.h -- this duplicates
X * the behavior of Vax-C
X * 31-Oct-84 ado/MM Parametized $ in indentifiers. Added a better
X * token concatenator and took out the trial
X * concatenation code. Also improved #ifdef code
X * and cleaned up the macro recursion tester.
X * 2-Nov-84 MM/ado Some bug fixes in token concatenation, also
X * a variety of minor (uninteresting) hacks.
X * 6-Nov-84 MM Happy Birthday. Broke into 4 files and added
X * #if sizeof (basic_types)
X * 9-Nov-84 MM Added -S* for pointer type sizes
X * 13-Nov-84 MM Split cpp1.c, added vms defaulting
X * 23-Nov-84 MM/ado -E supresses error exit, added CPP_INCLUDE,
X * fixed strncpy bug.
X * 3-Dec-84 ado/MM Added OLD_PREPROCESSOR
X * 7-Dec-84 MM Stuff in Nov 12 Draft Standard
X * 17-Dec-84 george Fixed problems with recursive macros
X * 17-Dec-84 MM Yet another attack on #if's (f/t)level removed.
X * 07-Jan-85 ado Init defines before doing command line options
X * so -Uunix works.
X */
X
X/*)BUILD
X $(PROGRAM) = cpp
X $(FILES) = { cpp1 cpp2 cpp3 cpp4 cpp5 cpp6 }
X $(INCLUDE) = { cppdef.h cpp.h }
X $(STACK) = 2000
X $(TKBOPTIONS) = {
X STACK = 2000
X }
X*/
X
X#ifdef DOCUMENTATION
X
Xtitle cpp C Pre-Processor
Xindex C pre-processor
X
Xsynopsis
X .s.nf
X cpp [-options] [infile [outfile]]
X .s.f
Xdescription
X
X CPP reads a C source file, expands macros and include
X files, and writes an input file for the C compiler.
X If no file arguments are given, CPP reads from stdin
X and writes to stdout. If one file argument is given,
X it will define the input file, while two file arguments
X define both input and output files. The file name "-"
X is a synonym for stdin or stdout as appropriate.
X
X The following options are supported. Options may
X be given in either case.
X .lm +16
X .p -16
X -C If set, source-file comments are written
X to the output file. This allows the output of CPP to be
X used as the input to a program, such as lint, that expects
X commands embedded in specially-formatted comments.
X .p -16
X -Dname=value Define the name as if the programmer wrote
X
X #define name value
X
X at the start of the first file. If "=value" is not
X given, a value of "1" will be used.
X
X On non-unix systems, all alphabetic text will be forced
X to upper-case.
X .p -16
X -E Always return "success" to the operating
X system, even if errors were detected. Note that some fatal
X errors, such as a missing #include file, will terminate
X CPP, returning "failure" even if the -E option is given.
X .p -16
X -Idirectory Add this directory to the list of
X directories searched for #include "..." and #include <...>
X commands. Note that there is no space between the
X "-I" and the directory string. More than one -I command
X is permitted. On non-Unix systems "directory" is forced
X to upper-case.
X .p -16
X -N CPP normally predefines some symbols defining
X the target computer and operating system. If -N is specified,
X no symbols will be predefined. If -N -N is specified, the
X "always present" symbols, __LINE__, __FILE__, and __DATE__
X are not defined.
X .p -16
X -Stext CPP normally assumes that the size of
X the target computer's basic variable types is the same as the size
X of these types of the host computer. (This can be overridden
X when CPP is compiled, however.) The -S option allows dynamic
X respecification of these values. "text" is a string of
X numbers, separated by commas, that specifies correct sizes.
X The sizes must be specified in the exact order:
X
X char short int long float double
X
X If you specify the option as "-S*text", pointers to these
X types will be specified. -S* takes one additional argument
X for pointer to function (e.g. int (*)())
X
X For example, to specify sizes appropriate for a PDP-11,
X you would write:
X
X c s i l f d func
X -S1,2,2,2,4,8,
X -S*2,2,2,2,2,2,2
X
X Note that all values must be specified.
X .p -16
X -Uname Undefine the name as if
X
X #undef name
X
X were given. On non-Unix systems, "name" will be forced to
X upper-case.
X .p -16
X -Xnumber Enable debugging code. If no value is
X given, a value of 1 will be used. (For maintenence of
X CPP only.)
X .s.lm -16
X
XPre-Defined Variables
X
X When CPP begins processing, the following variables will
X have been defined (unless the -N option is specified):
X .s
X Target computer (as appropriate):
X .s
X pdp11, vax, M68000 m68000 m68k
X .s
X Target operating system (as appropriate):
X .s
X rsx, rt11, vms, unix
X .s
X Target compiler (as appropriate):
X .s
X decus, vax11c
X .s
X The implementor may add definitions to this list.
X The default definitions match the definition of the
X host computer, operating system, and C compiler.
X .s
X The following are always available unless undefined (or
X -N was specified twice):
X .lm +16
X .p -12
X __FILE__ The input (or #include) file being compiled
X (as a quoted string).
X .p -12
X __LINE__ The line number being compiled.
X .p -12
X __DATE__ The date and time of compilation as
X a Unix ctime quoted string (the trailing newline is removed).
X Thus,
X .s
X printf("Bug at line %s,", __LINE__);
X printf(" source file %s", __FILE__);
X printf(" compiled on %s", __DATE__);
X .s.lm -16
X
XDraft Proposed Ansi Standard Considerations
X
X The current version of the Draft Proposed Standard
X explicitly states that "readers are requested not to specify
X or claim conformance to this draft." Readers and users
X of Decus CPP should not assume that Decus CPP conforms
X to the standard, or that it will conform to the actual
X C Language Standard.
X
X When CPP is itself compiled, many features of the Draft
X Proposed Standard that are incompatible with existing
X preprocessors may be disabled. See the comments in CPP's
X source for details.
X
X The latest version of the Draft Proposed Standard (as reflected
X in Decus CPP) is dated November 12, 1984.
X
X Comments are removed from the input text. The comment
X is replaced by a single space character. The -C option
X preserves comments, writing them to the output file.
X
X The '$' character is considered to be a letter. This is
X a permitted extension.
X
X The following new features of C are processed by CPP:
X .s.comment Note: significant spaces, not tabs, .br quotes #if, #elif
X .br;####_#elif expression (_#else _#if)
X .br;####'_\xNNN' (Hexadecimal constant)
X .br;####'_\a' (Ascii BELL)
X .br;####'_\v' (Ascii Vertical Tab)
X .br;####_#if defined NAME 1 if defined, 0 if not
X .br;####_#if defined (NAME) 1 if defined, 0 if not
X .br;####_#if sizeof (basic type)
X .br;####unary +
X .br;####123U, 123LU Unsigned ints and longs.
X .br;####12.3L Long double numbers
X .br;####token_#token Token concatenation
X .br;####_#include token Expands to filename
X
X The Draft Proposed Standard has extended C, adding a constant
X string concatenation operator, where
X
X "foo" "bar"
X
X is regarded as the single string "foobar". (This does not
X affect CPP's processing but does permit a limited form of
X macro argument substitution into strings as will be discussed.)
X
X The Standard Committee plans to add token concatenation
X to #define command lines. One suggested implementation
X is as follows: the sequence "Token1#Token2" is treated
X as if the programmer wrote "Token1Token2". This could
X be used as follows:
X
X #line 123
X #define ATLINE foo#__LINE__
X
X ATLINE would be defined as foo123.
X
X Note that "Token2" must either have the format of an
X identifier or be a string of digits. Thus, the string
X
X #define ATLINE foo#1x3
X
X generates two tokens: "foo1" and "x3".
X
X If the tokens T1 and T2 are concatenated into T3,
X this implementation operates as follows:
X
X 1. Expand T1 if it is a macro.
X 2. Expand T2 if it is a macro.
X 3. Join the tokens, forming T3.
X 4. Expand T3 if it is a macro.
X
X A macro formal parameter will be substituted into a string
X or character constant if it is the only component of that
X constant:
X
X #define VECSIZE 123
X #define vprint(name, size) \
X printf("name" "[" "size" "] = {\n")
X ... vprint(vector, VECSIZE);
X
X expands (effectively) to
X
X vprint("vector[123] = {\n");
X
X Note that this will be useful if your C compiler supports
X the new string concatenation operation noted above.
X As implemented here, if you write
X
X #define string(arg) "arg"
X ... string("foo") ...
X
X This implementation generates "foo", rather than the strictly
X correct ""foo"" (which will probably generate an error message).
X This is, strictly speaking, an error in CPP and may be removed
X from future releases.
X
Xerror messages
X
X Many. CPP prints warning or error messages if you try to
X use multiple-byte character constants (non-transportable)
X if you #undef a symbol that was not defined, or if your
X program has potentially nested comments.
X
Xauthor
X
X Martin Minow
X
Xbugs
X
X The #if expression processor uses signed integers only.
X I.e, #if 0xFFFFu < 0 may be TRUE.
X
X#endif
X
X#include <stdio.h>
X#include <ctype.h>
X#include "cppdef.h"
X#include "cpp.h"
X
X/*
X * Commonly used global variables:
X * line is the current input line number.
X * wrongline is set in many places when the actual output
X * line is out of sync with the numbering, e.g,
X * when expanding a macro with an embedded newline.
X *
X * token holds the last identifier scanned (which might
X * be a candidate for macro expansion).
X * errors is the running cpp error counter.
X * infile is the head of a linked list of input files (extended by
X * #include and macros being expanded). infile always points
X * to the current file/macro. infile->parent to the includer,
X * etc. infile->fd is NULL if this input stream is a macro.
X */
Xint line; /* Current line number */
Xint wrongline; /* Force #line to compiler */
Xchar token[IDMAX + 1]; /* Current input token */
Xint errors; /* cpp error counter */
XFILEINFO *infile = NULL; /* Current input file */
X#if DEBUG
Xint debug; /* TRUE if debugging now */
X#endif
X/*
X * This counter is incremented when a macro expansion is initiated.
X * If it exceeds a built-in value, the expansion stops -- this tests
X * for a runaway condition:
X * #define X Y
X * #define Y X
X * X
X * This can be disabled by falsifying rec_recover. (Nothing does this
X * currently: it is a hook for an eventual invocation flag.)
X */
Xint recursion; /* Infinite recursion counter */
Xint rec_recover = TRUE; /* Unwind recursive macros */
X
X/*
X * instring is set TRUE when a string is scanned. It modifies the
X * behavior of the "get next character" routine, causing all characters
X * to be passed to the caller (except <DEF_MAGIC>). Note especially that
X * comments and \<newline> are not removed from the source. (This
X * prevents cpp output lines from being arbitrarily long).
X *
X * inmacro is set by #define -- it absorbs comments and converts
X * form-feed and vertical-tab to space, but returns \<newline>
X * to the caller. Strictly speaking, this is a bug as \<newline>
X * shouldn't delimit tokens, but we'll worry about that some other
X * time -- it is more important to prevent infinitly long output lines.
X *
X * instring and inmarcor are parameters to the get() routine which
X * were made global for speed.
X */
Xint instring = FALSE; /* TRUE if scanning string */
Xint inmacro = FALSE; /* TRUE if #defining a macro */
X
X/*
X * work[] and workp are used to store one piece of text in a temporay
X * buffer. To initialize storage, set workp = work. To store one
X * character, call save(c); (This will fatally exit if there isn't
X * room.) To terminate the string, call save(EOS). Note that
X * the work buffer is used by several subroutines -- be sure your
X * data won't be overwritten. The extra byte in the allocation is
X * needed for string formal replacement.
X */
Xchar work[NWORK + 1]; /* Work buffer */
Xchar *workp; /* Work buffer pointer */
X
X/*
X * keepcomments is set TRUE by the -C option. If TRUE, comments
X * are written directly to the output stream. This is needed if
X * the output from cpp is to be passed to lint (which uses commands
X * embedded in comments). cflag contains the permanent state of the
X * -C flag. keepcomments is always falsified when processing #control
X * commands and when compilation is supressed by a false #if
X *
X * If eflag is set, CPP returns "success" even if non-fatal errors
X * were detected.
X *
X * If nflag is non-zero, no symbols are predefined except __LINE__.
X * __FILE__, and __DATE__. If nflag > 1, absolutely no symbols
X * are predefined.
X */
Xint keepcomments = FALSE; /* Write out comments flag */
Xint cflag = FALSE; /* -C option (keep comments) */
Xint eflag = FALSE; /* -E option (never fail) */
Xint nflag = 0; /* -N option (no predefines) */
X
X/*
X * ifstack[] holds information about nested #if's. It is always
X * accessed via *ifptr. The information is as follows:
X * WAS_COMPILING state of compiling flag at outer level.
X * ELSE_SEEN set TRUE when #else seen to prevent 2nd #else.
X * TRUE_SEEN set TRUE when #if or #elif succeeds
X * ifstack[0] holds the compiling flag. It is TRUE if compilation
X * is currently enabled. Note that this must be initialized TRUE.
X */
Xchar ifstack[BLK_NEST] = { TRUE }; /* #if information */
Xchar *ifptr = ifstack; /* -> current ifstack[] */
X
X/*
X * incdir[] stores the -i directories (and the system-specific
X * #include <...> directories.
X */
Xchar *incdir[NINCLUDE]; /* -i directories */
Xchar **incend = incdir; /* -> free space in incdir[] */
X
X/*
X * This is the table used to predefine target machine and operating
X * system designators. It may need hacking for specific circumstances.
X * Note: it is not clear that this is part of the Ansi Standard.
X * The -N option supresses preset definitions.
X */
Xchar *preset[] = { /* names defined at cpp start */
X#ifdef MACHINE
X MACHINE,
X#endif
X#ifdef SYSTEM
X SYSTEM,
X#endif
X#ifdef COMPILER
X COMPILER,
X#endif
X#if DEBUG
X "decus_cpp", /* Ourselves! */
X#endif
X NULL /* Must be last */
X};
X
X/*
X * The value of these predefined symbols must be recomputed whenever
X * they are evaluated. The order must not be changed.
X */
Xchar *magic[] = { /* Note: order is important */
X "__LINE__",
X "__FILE__",
X NULL /* Must be last */
X};
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X register int i;
X
X#if HOST == SYS_VMS
X argc = getredirection(argc, argv); /* vms >file and <file */
X#endif
X initdefines(); /* O.S. specific def's */
X i = dooptions(argc, argv); /* Command line -flags */
X switch (i) {
X case 3:
X /*
X * Get output file, "-" means use stdout.
X */
X if (!streq(argv[2], "-")) {
X#if HOST == SYS_VMS
X /*
X * On vms, reopen stdout with "vanilla rms" attributes.
X */
X if ((i = creat(argv[2], 0, "rat=cr", "rfm=var")) == -1
X || dup2(i, fileno(stdout)) == -1) {
X#else
X if (freopen(argv[2], "w", stdout) == NULL) {
X#endif
X perror(argv[2]);
X cerror("Can't open output file \"%s\"", argv[2]);
X exit(IO_ERROR);
X }
X } /* Continue by opening input */
X case 2: /* One file -> stdin */
X /*
X * Open input file, "-" means use stdin.
X */
X if (!streq(argv[1], "-")) {
X if (freopen(argv[1], "r", stdin) == NULL) {
X perror(argv[1]);
X cerror("Can't open input file \"%s\"", argv[1]);
X exit(IO_ERROR);
X }
X strcpy(work, argv[1]); /* Remember input filename */
X break;
X } /* Else, just get stdin */
X case 0: /* No args? */
X case 1: /* No files, stdin -> stdout */
X#if HOST == SYS_UNIX
X work[0] = EOS; /* Unix can't find stdin name */
X#else
X fgetname(stdin, work); /* Vax-11C, Decus C know name */
X#endif
X break;
X
X default:
X exit(IO_ERROR); /* Can't happen */
X }
X setincdirs(); /* Setup -I include directories */
X addfile(stdin, work); /* "open" main input file */
X#if DEBUG
X if (debug > 0)
X dumpdef("preset #define symbols");
X#endif
X cppmain(); /* Process main file */
X if ((i = (ifptr - &ifstack[0])) != 0) {
X#if OLD_PREPROCESSOR
X ciwarn("Inside #ifdef block at end of input, depth = %d", i);
X#else
X cierror("Inside #ifdef block at end of input, depth = %d", i);
X#endif
X }
X fclose(stdout);
X if (errors > 0) {
X fprintf(stderr, (errors == 1)
X ? "%d error in preprocessor\n"
X : "%d errors in preprocessor\n", errors);
X if (!eflag)
X exit(IO_ERROR);
X }
X exit(IO_NORMAL); /* No errors or -E option set */
X}
X
XFILE_LOCAL
Xcppmain()
X/*
X * Main process for cpp -- copies tokens from the current input
X * stream (main file, include file, or a macro) to the output
X * file.
X */
X{
X register int c; /* Current character */
X register int counter; /* newlines and spaces */
X extern int output(); /* Output one character */
X
X /*
X * Explicitly output a #line at the start of cpp output so
X * that lint (etc.) knows the name of the original source
X * file. If we don't do this explicitly, we may get
X * the name of the first #include file instead.
X */
X sharp();
X /*
X * This loop is started "from the top" at the beginning of each line
X * wrongline is set TRUE in many places if it is necessary to write
X * a #line record. (But we don't write them when expanding macros.)
X *
X * The counter variable has two different uses: at
X * the start of a line, it counts the number of blank lines that
X * have been skipped over. These are then either output via
X * #line records or by outputting explicit blank lines.
X * When expanding tokens within a line, the counter remembers
X * whether a blank/tab has been output. These are dropped
X * at the end of the line, and replaced by a single blank
X * within lines.
X */
X for (;;) {
X counter = 0; /* Count empty lines */
X for (;;) { /* For each line, ... */
X while (type[(c = get())] == SPA) /* Skip leading blanks */
X ; /* in this line. */
X if (c == '\n') /* If line's all blank, */
X ++counter; /* Do nothing now */
X else if (c == '#') { /* Is 1st non-space '#' */
X keepcomments = FALSE; /* Don't pass comments */
X counter = control(counter); /* Yes, do a #command */
X keepcomments = (cflag && compiling);
X }
X else if (c == EOF_CHAR) /* At end of file? */
X break;
X else if (!compiling) { /* #ifdef false? */
X skipnl(); /* Skip to newline */
X counter++; /* Count it, too. */
X }
X else {
X break; /* Actual token */
X }
X }
X if (c == EOF_CHAR) /* Exit process at */
X break; /* End of file */
X /*
X * If the loop didn't terminate because of end of file, we
X * know there is a token to compile. First, clean up after
X * absorbing newlines. counter has the number we skipped.
X */
X if ((wrongline && infile->fp != NULL) || counter > 4)
X sharp(); /* Output # line number */
X else { /* If just a few, stuff */
X while (--counter >= 0) /* them out ourselves */
X putchar('\n');
X }
X /*
X * Process each token on this line.
X */
X unget(); /* Reread the char. */
X for (;;) { /* For the whole line, */
X do { /* Token concat. loop */
X for (counter = 0; (type[(c = get())] == SPA);) {
X#if COMMENT_INVISIBLE
X if (c != COM_SEP)
X counter++;
X#else
X counter++; /* Skip over blanks */
X#endif
X }
X if (c == EOF_CHAR || c == '\n')
X goto end_line; /* Exit line loop */
X else if (counter > 0) /* If we got any spaces */
X putchar(' '); /* Output one space */
X c = macroid(c); /* Grab the token */
X } while (type[c] == LET && catenate());
X if (c == EOF_CHAR || c == '\n') /* From macro exp error */
X goto end_line; /* Exit line loop */
X switch (type[c]) {
X case LET:
X fputs(token, stdout); /* Quite ordinary token */
X break;
X
X
X case DIG: /* Output a number */
X case DOT: /* Dot may begin floats */
X scannumber(c, output);
X break;
X
X case QUO: /* char or string const */
X scanstring(c, output); /* Copy it to output */
X break;
X
X default: /* Some other character */
X cput(c); /* Just output it */
X break;
X } /* Switch ends */
X } /* Line for loop */
Xend_line: if (c == '\n') { /* Compiling at EOL? */
X putchar('\n'); /* Output newline, if */
X if (infile->fp == NULL) /* Expanding a macro, */
X wrongline = TRUE; /* Output # line later */
X }
X } /* Continue until EOF */
X}
X
Xoutput(c)
Xint c;
X/*
X * Output one character to stdout -- output() is passed as an
X * argument to scanstring()
X */
X{
X#if COMMENT_INVISIBLE
X if (c != TOK_SEP && c != COM_SEP)
X#else
X if (c != TOK_SEP)
X#endif
X putchar(c);
X}
X
Xstatic char *sharpfilename = NULL;
X
XFILE_LOCAL
Xsharp()
X/*
X * Output a line number line.
X */
X{
X register char *name;
X
X if (keepcomments) /* Make sure # comes on */
X putchar('\n'); /* a fresh, new line. */
X printf("#%s %d", LINE_PREFIX, line);
X if (infile->fp != NULL) {
X name = (infile->progname != NULL)
X ? infile->progname : infile->filename;
X if (sharpfilename == NULL
X || sharpfilename != NULL && !streq(name, sharpfilename)) {
X if (sharpfilename != NULL)
X free(sharpfilename);
X sharpfilename = savestring(name);
X printf(" \"%s\"", name);
X }
X }
X putchar('\n');
X wrongline = FALSE;
X}
END-of-cpp1.c
echo x - cpp3.c
sed 's/^X//' >cpp3.c << 'END-of-cpp3.c'
X/*
X * C P P 3 . C
X *
X * File open and command line options
X *
X * Edit history
X * 13-Nov-84 MM Split from cpp1.c
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include "cppdef.h"
X#include "cpp.h"
X#if DEBUG && (HOST == SYS_VMS || HOST == SYS_UNIX)
X#include <signal.h>
Xextern int abort(); /* For debugging */
X#endif
X
Xint
Xopenfile(filename)
Xchar *filename;
X/*
X * Open a file, add it to the linked list of open files.
X * This is called only from openfile() above.
X */
X{
X register FILE *fp;
X
X if ((fp = fopen(filename, "r")) == NULL) {
X#if DEBUG
X perror(filename);
X#endif
X return (FALSE);
X }
X#if DEBUG
X if (debug)
X fprintf(stderr, "Reading from \"%s\"\n", filename);
X#endif
X addfile(fp, filename);
X return (TRUE);
X}
X
Xaddfile(fp, filename)
XFILE *fp; /* Open file pointer */
Xchar *filename; /* Name of the file */
X/*
X * Initialize tables for this open file. This is called from openfile()
X * above (for #include files), and from the entry to cpp to open the main
X * input file. It calls a common routine, getfile() to build the FILEINFO
X * structure which is used to read characters. (getfile() is also called
X * to setup a macro replacement.)
X */
X{
X register FILEINFO *file;
X extern FILEINFO *getfile();
X
X file = getfile(NBUFF, filename);
X file->fp = fp; /* Better remember FILE * */
X file->buffer[0] = EOS; /* Initialize for first read */
X line = 1; /* Working on line 1 now */
X wrongline = TRUE; /* Force out initial #line */
X}
X
Xsetincdirs()
X/*
X * Append system-specific directories to the include directory list.
X * Called only when cpp is started.
X */
X{
X
X#ifdef CPP_INCLUDE
X *incend++ = CPP_INCLUDE;
X#define IS_INCLUDE 1
X#else
X#define IS_INCLUDE 0
X#endif
X
X#if HOST == SYS_UNIX
X *incend++ = "/usr/include";
X#define MAXINCLUDE (NINCLUDE - 1 - IS_INCLUDE)
X#endif
X
X#if HOST == SYS_VMS
X extern char *getenv();
X
X if (getenv("C$LIBRARY") != NULL)
X *incend++ = "C$LIBRARY:";
X *incend++ = "SYS$LIBRARY:";
X#define MAXINCLUDE (NINCLUDE - 2 - IS_INCLUDE)
X#endif
X
X#if HOST == SYS_RSX
X extern int $$rsts; /* TRUE on RSTS/E */
X extern int $$pos; /* TRUE on PRO-350 P/OS */
X extern int $$vms; /* TRUE on VMS compat. */
X
X if ($$pos) { /* P/OS? */
X *incend++ = "SY:[ZZDECUSC]"; /* C #includes */
X *incend++ = "LB:[1,5]"; /* RSX library */
X }
X else if ($$rsts) { /* RSTS/E? */
X *incend++ = "SY:@"; /* User-defined account */
X *incend++ = "C:"; /* Decus-C library */
X *incend++ = "LB:[1,1]"; /* RSX library */
X }
X else if ($$vms) { /* VMS compatibility? */
X *incend++ = "C:";
X }
X else { /* Plain old RSX/IAS */
X *incend++ = "LB:[1,1]";
X }
X#define MAXINCLUDE (NINCLUDE - 3 - IS_INCLUDE)
X#endif
X
X#if HOST == SYS_RT11
X extern int $$rsts; /* RSTS/E emulation? */
X
X if ($$rsts)
X *incend++ = "SY:@"; /* User-defined account */
X *incend++ = "C:"; /* Decus-C library disk */
X *incend++ = "SY:"; /* System (boot) disk */
X#define MAXINCLUDE (NINCLUDE - 3 - IS_INCLUDE)
X#endif
X}
X
Xint
Xdooptions(argc, argv)
Xint argc;
Xchar *argv[];
X/*
X * dooptions is called to process command line arguments (-Detc).
X * It is called only at cpp startup.
X */
X{
X register char *ap;
X register DEFBUF *dp;
X register int c;
X int i, j;
X char *arg;
X SIZES *sizp; /* For -S */
X int size; /* For -S */
X int isdatum; /* FALSE for -S* */
X int endtest; /* For -S */
X
X for (i = j = 1; i < argc; i++) {
X arg = ap = argv[i];
X if (*ap++ != '-' || *ap == EOS)
X argv[j++] = argv[i];
X else {
X c = *ap++; /* Option byte */
X if (islower(c)) /* Normalize case */
X c = toupper(c);
X switch (c) { /* Command character */
X case 'C': /* Keep comments */
X cflag = TRUE;
X keepcomments = TRUE;
X break;
X
X case 'D': /* Define symbol */
X#if HOST != SYS_UNIX
X zap_uc(ap); /* Force define to U.C. */
X#endif
X /*
X * If the option is just "-Dfoo", make it -Dfoo=1
X */
X while (*ap != EOS && *ap != '=')
X ap++;
X if (*ap == EOS)
X ap = "1";
X else
X *ap++ = EOS;
X /*
X * Now, save the word and its definition.
X */
X dp = defendel(argv[i] + 2, FALSE);
X dp->repl = savestring(ap);
X dp->nargs = DEF_NOARGS;
X break;
X
X case 'E': /* Ignore non-fatal */
X eflag = TRUE; /* errors. */
X break;
X
X case 'I': /* Include directory */
X if (incend >= &incdir[MAXINCLUDE])
X cfatal("Too many include directories", NULLST);
X *incend++ = ap;
X break;
X
X case 'N': /* No predefineds */
X nflag++; /* Repeat to undefine */
X break; /* __LINE__, etc. */
X
X case 'S':
X sizp = size_table;
X if (isdatum = (*ap != '*')) /* If it's just -S, */
X endtest = T_FPTR; /* Stop here */
X else { /* But if it's -S* */
X ap++; /* Step over '*' */
X endtest = 0; /* Stop at end marker */
X }
X while (sizp->bits != endtest && *ap != EOS) {
X if (!isdigit(*ap)) { /* Skip to next digit */
X ap++;
X continue;
X }
X size = 0; /* Compile the value */
X while (isdigit(*ap)) {
X size *= 10;
X size += (*ap++ - '0');
X }
X if (isdatum)
X sizp->size = size; /* Datum size */
X else
X sizp->psize = size; /* Pointer size */
X sizp++;
X }
X if (sizp->bits != endtest)
X cwarn("-S, too few values specified in %s", argv[i]);
X else if (*ap != EOS)
X cwarn("-S, too many values, \"%s\" unused", ap);
X break;
X
X case 'U': /* Undefine symbol */
X#if HOST != SYS_UNIX
X zap_uc(ap);
X#endif
X if (defendel(ap, TRUE) == NULL)
X cwarn("\"%s\" wasn't defined", ap);
X break;
X
X#if DEBUG
X case 'X': /* Debug */
X debug = (isdigit(*ap)) ? atoi(ap) : 1;
X#if (HOST == SYS_VMS || HOST == SYS_UNIX)
X signal(SIGINT, abort); /* Trap "interrupt" */
X#endif
X fprintf(stderr, "Debug set to %d\n", debug);
X break;
X#endif
X
X default: /* What is this one? */
X cwarn("Unknown option \"%s\"", arg);
X fprintf(stderr, "The following options are valid:\n\
X -C\t\t\tWrite source file comments to output\n\
X -Dsymbol=value\tDefine a symbol with the given (optional) value\n\
X -Idirectory\t\tAdd a directory to the #include search list\n\
X -N\t\t\tDon't predefine target-specific names\n\
X -Stext\t\tSpecify sizes for #if sizeof\n\
X -Usymbol\t\tUndefine symbol\n");
X#if DEBUG
X fprintf(stderr, " -Xvalue\t\tSet internal debug flag\n");
X#endif
X break;
X } /* Switch on all options */
X } /* If it's a -option */
X } /* For all arguments */
X if (j > 3) {
X cerror(
X "Too many file arguments. Usage: cpp [input [output]]",
X NULLST);
X }
X return (j); /* Return new argc */
X}
X
X#if HOST != SYS_UNIX
XFILE_LOCAL
Xzap_uc(ap)
Xregister char *ap;
X/*
X * Dec operating systems mangle upper-lower case in command lines.
X * This routine forces the -D and -U arguments to uppercase.
X * It is called only on cpp startup by dooptions().
X */
X{
X while (*ap != EOS) {
X /*
X * Don't use islower() here so it works with Multinational
X */
X if (*ap >= 'a' && *ap <= 'z')
X *ap = toupper(*ap);
X ap++;
X }
X}
X#endif
X
Xinitdefines()
X/*
X * Initialize the built-in #define's. There are two flavors:
X * #define decus 1 (static definitions)
X * #define __FILE__ ?? (dynamic, evaluated by magic)
X * Called only on cpp startup.
X *
X * Note: the built-in static definitions are supressed by the -N option.
X * __LINE__, __FILE__, and __DATE__ are always present.
X */
X{
X register char **pp;
X register char *tp;
X register DEFBUF *dp;
X int i;
X long tvec;
X extern char *ctime();
X
X /*
X * Predefine the built-in symbols. Allow the
X * implementor to pre-define a symbol as "" to
X * eliminate it.
X */
X if (nflag == 0) {
X for (pp = preset; *pp != NULL; pp++) {
X if (*pp[0] != EOS) {
X dp = defendel(*pp, FALSE);
X dp->repl = savestring("1");
X dp->nargs = DEF_NOARGS;
X }
X }
X }
X /*
X * The magic pre-defines (__FILE__ and __LINE__ are
X * initialized with negative argument counts. expand()
X * notices this and calls the appropriate routine.
X * DEF_NOARGS is one greater than the first "magic" definition.
X */
X if (nflag < 2) {
X for (pp = magic, i = DEF_NOARGS; *pp != NULL; pp++) {
X dp = defendel(*pp, FALSE);
X dp->nargs = --i;
X }
X#if OK_DATE
X /*
X * Define __DATE__ as today's date.
X */
X dp = defendel("__DATE__", FALSE);
X dp->repl = tp = getmem(27);
X dp->nargs = DEF_NOARGS;
X time(&tvec);
X *tp++ = '"';
X strcpy(tp, ctime(&tvec));
X tp[24] = '"'; /* Overwrite newline */
X#endif
X }
X}
X
X#if HOST == SYS_VMS
X/*
X * getredirection() is intended to aid in porting C programs
X * to VMS (Vax-11 C) which does not support '>' and '<'
X * I/O redirection. With suitable modification, it may
X * useful for other portability problems as well.
X */
X
Xint
Xgetredirection(argc, argv)
Xint argc;
Xchar **argv;
X/*
X * Process vms redirection arg's. Exit if any error is seen.
X * If getredirection() processes an argument, it is erased
X * from the vector. getredirection() returns a new argc value.
X *
X * Warning: do not try to simplify the code for vms. The code
X * presupposes that getredirection() is called before any data is
X * read from stdin or written to stdout.
X *
X * Normal usage is as follows:
X *
X * main(argc, argv)
X * int argc;
X * char *argv[];
X * {
X * argc = getredirection(argc, argv);
X * }
X */
X{
X register char *ap; /* Argument pointer */
X int i; /* argv[] index */
X int j; /* Output index */
X int file; /* File_descriptor */
X extern int errno; /* Last vms i/o error */
X
X for (j = i = 1; i < argc; i++) { /* Do all arguments */
X switch (*(ap = argv[i])) {
X case '<': /* <file */
X if (freopen(++ap, "r", stdin) == NULL) {
X perror(ap); /* Can't find file */
X exit(errno); /* Is a fatal error */
X }
X break;
X
X case '>': /* >file or >>file */
X if (*++ap == '>') { /* >>file */
X /*
X * If the file exists, and is writable by us,
X * call freopen to append to the file (using the
X * file's current attributes). Otherwise, create
X * a new file with "vanilla" attributes as if the
X * argument was given as ">filename".
X * access(name, 2) returns zero if we can write on
X * the specified file.
X */
X if (access(++ap, 2) == 0) {
X if (freopen(ap, "a", stdout) != NULL)
X break; /* Exit case statement */
X perror(ap); /* Error, can't append */
X exit(errno); /* After access test */
X } /* If file accessable */
X }
X /*
X * On vms, we want to create the file using "standard"
X * record attributes. creat(...) creates the file
X * using the caller's default protection mask and
X * "variable length, implied carriage return"
X * attributes. dup2() associates the file with stdout.
X */
X if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1
X || dup2(file, fileno(stdout)) == -1) {
X perror(ap); /* Can't create file */
X exit(errno); /* is a fatal error */
X } /* If '>' creation */
X break; /* Exit case test */
X
X default:
X argv[j++] = ap; /* Not a redirector */
X break; /* Exit case test */
X }
X } /* For all arguments */
X argv[j] = NULL; /* Terminate argv[] */
X return (j); /* Return new argc */
X}
X#endif
X
X
X
END-of-cpp3.c
echo x - cpp4.c
sed 's/^X//' >cpp4.c << 'END-of-cpp4.c'
X/*
X * C P P 4 . C
X * M a c r o D e f i n i t i o n s
X *
X * Edit History
X * 31-Aug-84 MM USENET net.sources release
X * 04-Oct-84 MM __LINE__ and __FILE__ must call ungetstring()
X * so they work correctly with token concatenation.
X * Added string formal recognition.
X * 25-Oct-84 MM "Short-circuit" evaluate #if's so that we
X * don't print unnecessary error messages for
X * #if !defined(FOO) && FOO != 0 && 10 / FOO ...
X * 31-Oct-84 ado/MM Added token concatenation
X * 6-Nov-84 MM Split off eval stuff
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include "cppdef.h"
X#include "cpp.h"
X/*
X * parm[], parmp, and parlist[] are used to store #define() argument
X * lists. nargs contains the actual number of parameters stored.
X */
Xstatic char parm[NPARMWORK + 1]; /* define param work buffer */
Xstatic char *parmp; /* Free space in parm */
Xstatic char *parlist[LASTPARM]; /* -> start of each parameter */
Xstatic int nargs; /* Parameters for this macro */
X
Xdodefine()
X/*
X * Called from control when a #define is scanned. This module
X * parses formal parameters and the replacement string. When
X * the formal parameter name is encountered in the replacement
X * string, it is replaced by a character in the range 128 to
X * 128+NPARAM (this allows up to 32 parameters within the
X * Dec Multinational range). If cpp is ported to an EBCDIC
X * machine, you will have to make other arrangements.
X *
X * There is some special case code to distinguish
X * #define foo bar
X * from #define foo() bar
X *
X * Also, we make sure that
X * #define foo foo
X * expands to "foo" but doesn't put cpp into an infinite loop.
X *
X * A warning message is printed if you redefine a symbol to a
X * different text. I.e,
X * #define foo 123
X * #define foo 123
X * is ok, but
X * #define foo 123
X * #define foo +123
X * is not.
X *
X * The following subroutines are called from define():
X * checkparm called when a token is scanned. It checks through the
X * array of formal parameters. If a match is found, the
X * token is replaced by a control byte which will be used
X * to locate the parameter when the macro is expanded.
X * textput puts a string in the macro work area (parm[]), updating
X * parmp to point to the first free byte in parm[].
X * textput() tests for work buffer overflow.
X * charput puts a single character in the macro work area (parm[])
X * in a manner analogous to textput().
X */
X{
X register int c;
X register DEFBUF *dp; /* -> new definition */
X int isredefine; /* TRUE if redefined */
X char *old; /* Remember redefined */
X extern int save(); /* Save char in work[] */
X
X if (type[(c = skipws())] != LET)
X goto bad_define;
X isredefine = FALSE; /* Set if redefining */
X if ((dp = lookid(c)) == NULL) /* If not known now */
X dp = defendel(token, FALSE); /* Save the name */
X else { /* It's known: */
X isredefine = TRUE; /* Remember this fact */
X old = dp->repl; /* Remember replacement */
X dp->repl = NULL; /* No replacement now */
X }
X parlist[0] = parmp = parm; /* Setup parm buffer */
X if ((c = get()) == '(') { /* With arguments? */
X nargs = 0; /* Init formals counter */
X do { /* Collect formal parms */
X if (nargs >= LASTPARM)
X cfatal("Too many arguments for macro", NULLST);
X else if ((c = skipws()) == ')')
X break; /* Got them all */
X else if (type[c] != LET) /* Bad formal syntax */
X goto bad_define;
X scanid(c); /* Get the formal param */
X parlist[nargs++] = parmp; /* Save its start */
X textput(token); /* Save text in parm[] */
X } while ((c = skipws()) == ','); /* Get another argument */
X if (c != ')') /* Must end at ) */
X goto bad_define;
X c = ' '; /* Will skip to body */
X }
X else {
X /*
X * DEF_NOARGS is needed to distinguish between
X * "#define foo" and "#define foo()".
X */
X nargs = DEF_NOARGS; /* No () parameters */
X }
X if (type[c] == SPA) /* At whitespace? */
X c = skipws(); /* Not any more. */
X workp = work; /* Replacement put here */
X inmacro = TRUE; /* Keep \<newline> now */
X while (c != EOF_CHAR && c != '\n') { /* Compile macro body */
X#if OK_CONCAT
X if (c == '#') { /* Token concatenation? */
X while (workp > work && type[workp[-1]] == SPA)
X --workp; /* Erase leading spaces */
X save(TOK_SEP); /* Stuff a delimiter */
X c = skipws(); /* Eat whitespace */
X if (type[c] == LET) /* Another token here? */
X ; /* Stuff it normally */
X else if (type[c] == DIG) { /* Digit string after? */
X while (type[c] == DIG) { /* Stuff the digits */
X save(c);
X c = get();
X }
X save(TOK_SEP); /* Delimit 2nd token */
X }
X else {
X ciwarn("Strange character after # (%d.)", c);
X }
X continue;
X }
X#endif
X switch (type[c]) {
X case LET:
X checkparm(c, dp); /* Might be a formal */
X break;
X
X case DIG: /* Number in mac. body */
X case DOT: /* Maybe a float number */
X scannumber(c, save); /* Scan it off */
X break;
X
X case QUO: /* String in mac. body */
X#if STRING_FORMAL
X stparmscan(c, dp); /* Do string magic */
X#else
X stparmscan(c);
X#endif
X break;
X
X case BSH: /* Backslash */
X save('\\');
X if ((c = get()) == '\n')
X wrongline = TRUE;
X save(c);
X break;
X
X case SPA: /* Absorb whitespace */
X /*
X * Note: the "end of comment" marker is passed on
X * to allow comments to separate tokens.
X */
X if (workp[-1] == ' ') /* Absorb multiple */
X break; /* spaces */
X else if (c == '\t')
X c = ' '; /* Normalize tabs */
X /* Fall through to store character */
X default: /* Other character */
X save(c);
X break;
X }
X c = get();
X }
X inmacro = FALSE; /* Stop newline hack */
X unget(); /* For control check */
X if (workp > work && workp[-1] == ' ') /* Drop trailing blank */
X workp--;
X *workp = EOS; /* Terminate work */
X dp->repl = savestring(work); /* Save the string */
X dp->nargs = nargs; /* Save arg count */
X#if DEBUG
X if (debug)
X dumpadef("macro definition", dp);
X#endif
X if (isredefine) { /* Error if redefined */
X if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl))
X || (old == NULL && dp->repl != NULL)
X || (old != NULL && dp->repl == NULL)) {
X cerror("Redefining defined variable \"%s\"", dp->name);
X }
X if (old != NULL) /* We don't need the */
X free(old); /* old definition now. */
X }
X return;
X
Xbad_define:
X cerror("#define syntax error", NULLST);
X inmacro = FALSE; /* Stop <newline> hack */
X}
X
Xcheckparm(c, dp)
Xregister int c;
XDEFBUF *dp;
X/*
X * Replace this param if it's defined. Note that the macro name is a
X * possible replacement token. We stuff DEF_MAGIC in front of the token
X * which is treated as a LETTER by the token scanner and eaten by
X * the output routine. This prevents the macro expander from
X * looping if someone writes "#define foo foo".
X */
X{
X register int i;
X register char *cp;
X
X scanid(c); /* Get parm to token[] */
X for (i = 0; i < nargs; i++) { /* For each argument */
X if (streq(parlist[i], token)) { /* If it's known */
X save(i + MAC_PARM); /* Save a magic cookie */
X return; /* And exit the search */
X }
X }
X if (streq(dp->name, token)) /* Macro name in body? */
X save(DEF_MAGIC); /* Save magic marker */
X for (cp = token; *cp != EOS;) /* And save */
X save(*cp++); /* The token itself */
X}
X
X#if STRING_FORMAL
Xstparmscan(delim, dp)
Xint delim;
Xregister DEFBUF *dp;
X/*
X * Scan the string (starting with the given delimiter).
X * The token is replaced if it is the only text in this string or
X * character constant. The algorithm follows checkparm() above.
X * Note that scanstring() has approved of the string.
X */
X{
X register int c;
X
X /*
X * Warning -- this code hasn't been tested for a while.
X * It exists only to preserve compatibility with earlier
X * implementations of cpp. It is not part of the Draft
X * ANSI Standard C language.
X */
X save(delim);
X instring = TRUE;
X while ((c = get()) != delim
X && c != '\n'
X && c != EOF_CHAR) {
X if (type[c] == LET) /* Maybe formal parm */
X checkparm(c, dp);
X else {
X save(c);
X if (c == '\\')
X save(get());
X }
X }
X instring = FALSE;
X if (c != delim)
X cerror("Unterminated string in macro body", NULLST);
X save(c);
X}
X#else
Xstparmscan(delim)
Xint delim;
X/*
X * Normal string parameter scan.
X */
X{
X register char *wp;
X register int i;
X extern int save();
X
X wp = workp; /* Here's where it starts */
X if (!scanstring(delim, save))
X return; /* Exit on scanstring error */
X workp[-1] = EOS; /* Erase trailing quote */
X wp++; /* -> first string content byte */
X for (i = 0; i < nargs; i++) {
X if (streq(parlist[i], wp)) {
X *wp++ = MAC_PARM + PAR_MAC; /* Stuff a magic marker */
X *wp++ = (i + MAC_PARM); /* Make a formal marker */
X *wp = wp[-3]; /* Add on closing quote */
X workp = wp + 1; /* Reset string end */
X return;
X }
X }
X workp[-1] = wp[-1]; /* Nope, reset end quote. */
X}
X#endif
X
Xdoundef()
X/*
X * Remove the symbol from the defined list.
X * Called from the #control processor.
X */
X{
X register int c;
X
X if (type[(c = skipws())] != LET)
X cerror("Illegal #undef argument", NULLST);
X else {
X scanid(c); /* Get name to token[] */
X if (defendel(token, TRUE) == NULL) {
X cwarn("Symbol \"%s\" not defined in #undef", token);
X }
X }
X}
X
Xtextput(text)
Xchar *text;
X/*
X * Put the string in the parm[] buffer.
X */
X{
X register int size;
X
X size = strlen(text) + 1;
X if ((parmp + size) >= &parm[NPARMWORK])
X cfatal("Macro work area overflow", NULLST);
X else {
X strcpy(parmp, text);
X parmp += size;
X }
X}
X
Xcharput(c)
Xregister int c;
X/*
X * Put the byte in the parm[] buffer.
X */
X{
X if (parmp >= &parm[NPARMWORK])
X cfatal("Macro work area overflow", NULLST);
X else {
X *parmp++ = c;
X }
X}
X
X/*
X * M a c r o E x p a n s i o n
X */
X
Xstatic DEFBUF *macro; /* Catches start of infinite macro */
X
Xexpand(tokenp)
Xregister DEFBUF *tokenp;
X/*
X * Expand a macro. Called from the cpp mainline routine (via subroutine
X * macroid()) when a token is found in the symbol table. It calls
X * expcollect() to parse actual parameters, checking for the correct number.
X * It then creates a "file" containing a single line containing the
X * macro with actual parameters inserted appropriately. This is
X * "pushed back" onto the input stream. (When the get() routine runs
X * off the end of the macro line, it will dismiss the macro itself.)
X */
X{
X register int c;
X register FILEINFO *file;
X extern FILEINFO *getfile();
X
X#if DEBUG
X if (debug)
X dumpadef("expand entry", tokenp);
X#endif
X /*
X * If no macro is pending, save the name of this macro
X * for an eventual error message.
X */
X if (recursion++ == 0)
X macro = tokenp;
X else if (recursion == RECURSION_LIMIT) {
X cerror("Recursive macro definition of \"%s\"", tokenp->name);
X fprintf(stderr, "(Defined by \"%s\")\n", macro->name);
X if (rec_recover) {
X do {
X c = get();
X } while (infile != NULL && infile->fp == NULL);
X unget();
X recursion = 0;
X return;
X }
X }
X /*
X * Here's a macro to expand.
X */
X nargs = 0; /* Formals counter */
X parmp = parm; /* Setup parm buffer */
X switch (tokenp->nargs) {
X case (-2): /* __LINE__ */
X sprintf(work, "%d", line);
X ungetstring(work);
X break;
X
X case (-3): /* __FILE__ */
X for (file = infile; file != NULL; file = file->parent) {
X if (file->fp != NULL) {
X sprintf(work, "\"%s\"", (file->progname != NULL)
X ? file->progname : file->filename);
X ungetstring(work);
X break;
X }
X }
X break;
X
X default:
X /*
X * Nothing funny about this macro.
X */
X if (tokenp->nargs < 0)
X cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name);
X while ((c = skipws()) == '\n') /* Look for (, skipping */
X wrongline = TRUE; /* spaces and newlines */
X if (c != '(') {
X /*
X * If the programmer writes
X * #define foo() ...
X * ...
X * foo [no ()]
X * just write foo to the output stream.
X */
X unget();
X cwarn("Macro \"%s\" needs arguments", tokenp->name);
X fputs(tokenp->name, stdout);
X return;
X }
X else if (expcollect()) { /* Collect arguments */
X if (tokenp->nargs != nargs) { /* Should be an error? */
X cwarn("Wrong number of macro arguments for \"%s\"",
X tokenp->name);
X }
X#if DEBUG
X if (debug)
X dumpparm("expand");
X#endif
X } /* Collect arguments */
X case DEF_NOARGS: /* No parameters just stuffs */
X expstuff(tokenp); /* Do actual parameters */
X } /* nargs switch */
X}
X
XFILE_LOCAL int
Xexpcollect()
X/*
X * Collect the actual parameters for this macro. TRUE if ok.
X */
X{
X register int c;
X register int paren; /* For embedded ()'s */
X extern int charput();
X
X for (;;) {
X paren = 0; /* Collect next arg. */
X while ((c = skipws()) == '\n') /* Skip over whitespace */
X wrongline = TRUE; /* and newlines. */
X if (c == ')') { /* At end of all args? */
X /*
X * Note that there is a guard byte in parm[]
X * so we don't have to check for overflow here.
X */
X *parmp = EOS; /* Make sure terminated */
X break; /* Exit collection loop */
X }
X else if (nargs >= LASTPARM)
X cfatal("Too many arguments in macro expansion", NULLST);
X parlist[nargs++] = parmp; /* At start of new arg */
X for (;; c = cget()) { /* Collect arg's bytes */
X if (c == EOF_CHAR) {
X cerror("end of file within macro argument", NULLST);
X return (FALSE); /* Sorry. */
X }
X else if (c == '\\') { /* Quote next character */
X charput(c); /* Save the \ for later */
X charput(cget()); /* Save the next char. */
X continue; /* And go get another */
X }
X else if (type[c] == QUO) { /* Start of string? */
X scanstring(c, charput); /* Scan it off */
X continue; /* Go get next char */
X }
X else if (c == '(') /* Worry about balance */
X paren++; /* To know about commas */
X else if (c == ')') { /* Other side too */
X if (paren == 0) { /* At the end? */
X unget(); /* Look at it later */
X break; /* Exit arg getter. */
X }
X paren--; /* More to come. */
X }
X else if (c == ',' && paren == 0) /* Comma delimits args */
X break;
X else if (c == '\n') /* Newline inside arg? */
X wrongline = TRUE; /* We'll need a #line */
X charput(c); /* Store this one */
X } /* Collect an argument */
X charput(EOS); /* Terminate argument */
X#if DEBUG
X if (debug)
X printf("parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]);
X#endif
X } /* Collect all args. */
X return (TRUE); /* Normal return */
X}
X
XFILE_LOCAL
Xexpstuff(tokenp)
XDEFBUF *tokenp; /* Current macro being expanded */
X/*
X * Stuff the macro body, replacing formal parameters by actual parameters.
X */
X{
X register int c; /* Current character */
X register char *inp; /* -> repl string */
X register char *defp; /* -> macro output buff */
X int size; /* Actual parm. size */
X char *defend; /* -> output buff end */
X int string_magic; /* String formal hack */
X FILEINFO *file; /* Funny #include */
X extern FILEINFO *getfile();
X
X file = getfile(NBUFF, tokenp->name);
X inp = tokenp->repl; /* -> macro replacement */
X defp = file->buffer; /* -> output buffer */
X defend = defp + (NBUFF - 1); /* Note its end */
X if (inp != NULL) {
X while ((c = (*inp++ & 0xFF)) != EOS) {
X if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC)) {
X string_magic = (c == (MAC_PARM + PAR_MAC));
X if (string_magic)
X c = (*inp++ & 0xFF);
X /*
X * Replace formal parameter by actual parameter string.
X */
X if ((c -= MAC_PARM) < nargs) {
X size = strlen(parlist[c]);
X if ((defp + size) >= defend)
X goto nospace;
X /*
X * Erase the extra set of quotes.
X */
X if (string_magic && defp[-1] == parlist[c][0]) {
X strcpy(defp-1, parlist[c]);
X defp += (size - 2);
X }
X else {
X strcpy(defp, parlist[c]);
X defp += size;
X }
X }
X }
X else if (defp >= defend) {
Xnospace: cfatal("Out of space in macro \"%s\" arg expansion",
X tokenp->name);
X }
X else {
X *defp++ = c;
X }
X }
X }
X *defp = EOS;
X#if DEBUG
X if (debug > 1)
X printf("macroline: \"%s\"\n", file->buffer);
X#endif
X}
X
X#if DEBUG
Xdumpparm(why)
Xchar *why;
X/*
X * Dump parameter list.
X */
X{
X register int i;
X
X printf("dump of %d parameters (%d bytes total) %s\n",
X nargs, parmp - parm, why);
X for (i = 0; i < nargs; i++) {
X printf("parm[%d] (%d) = \"%s\"\n",
X i + 1, strlen(parlist[i]), parlist[i]);
X }
X}
X#endif
END-of-cpp4.c
exit
|