Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

676 lines
22 KiB

  1. /************************************************************************/
  2. /* */
  3. /* RCPP - Resource Compiler Pre-Processor for NT system */
  4. /* */
  5. /* P0IO.C - Input/Output for Preprocessor */
  6. /* */
  7. /* 27-Nov-90 w-BrianM Update for NT from PM SDK RCPP */
  8. /* */
  9. /************************************************************************/
  10. #include "rc.h"
  11. /************************************************************************/
  12. /* Local Function Prototypes */
  13. /************************************************************************/
  14. PWCHAR esc_sequence(PWCHAR, PWCHAR);
  15. #define TEXT_TYPE ptext_t
  16. /*** ASSUME : the trailing marker byte is only 1 character. ***/
  17. #define PUSHBACK_BYTES 1
  18. #define TRAILING_BYTES 1
  19. #define EXTRA_BYTES (PUSHBACK_BYTES + TRAILING_BYTES)
  20. /*
  21. ** here are some defines for the new handling of io buffers.
  22. ** the buffer itself is 6k plus some extra bytes.
  23. ** the main source file uses all 6k.
  24. ** the first level of include files will use 4k starting 2k from the beginning.
  25. ** the 2nd level - n level will use 2k starting 4k from the beginning.
  26. ** this implies that some special handling may be necessary when we get
  27. ** overlapping buffers. (unless the source file itself is < 2k
  28. ** all the include files are < 2k and they do not nest more than 2 deep.)
  29. ** first, the source file is read into the buffer (6k at a time).
  30. ** at the first include file, (if the source from the parent file
  31. ** is more than 2k chars) . . .
  32. ** if the Current_char ptr is not pointing above the 2k boundary
  33. ** (which is the beginning of the buffer for the include file)
  34. ** then we pretend we've read in only 2k into the buffer and
  35. ** place the terminator at the end of the parents 2k buffer.
  36. ** else we pretend we've used up all chars in the parents buffer
  37. ** so the next read for the parent will be the terminator, and
  38. ** the buffer will get filled in the usual manner.
  39. ** (if we're in a macro, the picture is slightly different in that we have
  40. ** to update the 'real' source file pointer in the macro structure.)
  41. **
  42. ** the first nested include file is handled in a similar manner. (except
  43. ** it starts up 4k away from the start.)
  44. **
  45. ** any further nesting will keep overlaying the upper 2k part.
  46. */
  47. #define IO_BLOCK (4 * 1024 + EXTRA_BYTES)
  48. int vfCurrFileType = DFT_FILE_IS_UNKNOWN; //- Added for 16-bit file support.
  49. extern expansion_t Macro_expansion[];
  50. typedef struct s_filelist filelist_t;
  51. static struct s_filelist { /* list of input files (nested) */
  52. int fl_bufsiz; /* characters to read into the buffer */
  53. FILE * fl_file; /* FILE id */
  54. long fl_lineno; /* line number when file was pushed */
  55. PWCHAR fl_name; /* previous file text name */
  56. ptext_t fl_currc; /* ptr into our buffer for current c */
  57. TEXT_TYPE fl_buffer; /* type of buffer */
  58. int fl_numread; /* # of characters read from the file */
  59. int fl_fFileType; //- Added for 16-bit file support.
  60. //- return from DetermineFileType.
  61. long fl_seek; //- Added for restart - contains seek
  62. // address of last read.
  63. } Fstack[LIMIT_NESTED_INCLUDES];
  64. static FILE *Fp = NULL;
  65. int Findex = -1;
  66. /************************************************************************
  67. * NEWINPUT - A new input file is to be opened, saving the old.
  68. *
  69. * ARGUMENTS
  70. * WCHAR *newname - the name of the file
  71. *
  72. * RETURNS - none
  73. *
  74. * SIDE EFFECTS
  75. * - causes input stream to be switched
  76. * - Linenumber is reset to 1
  77. * - storage is allocated for the newname
  78. * - Filename is set to the new name
  79. *
  80. * DESCRIPTION
  81. * The file is opened, and if successful, the current input stream is saved
  82. * and the stream is switched to the new file. If the newname is NULL,
  83. * then stdin is taken as the new input.
  84. *
  85. * AUTHOR - Ralph Ryan, Sept. 9, 1982
  86. *
  87. * MODIFICATIONS - none
  88. *
  89. ************************************************************************/
  90. int
  91. newinput (
  92. WCHAR *newname,
  93. int m_open
  94. )
  95. {
  96. filelist_t *pF;
  97. WCHAR *p;
  98. if( newname == NULL ) {
  99. Fp = stdin;
  100. }
  101. else {
  102. // Note: Always use the Ansi codepage here. uiCodePage may have been
  103. // modified by a codepage pragma in the source file.
  104. WideCharToMultiByte (GetACP(), 0, newname, -1, chBuf, sizeof(chBuf), NULL, NULL);
  105. if((Fp = fopen(chBuf, "rb")) == NULL) {
  106. if(m_open == MUST_OPEN) {
  107. Msg_Temp = GET_MSG (1005);
  108. SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, chBuf);
  109. fatal(1005);
  110. }
  111. return(FALSE);
  112. }
  113. }
  114. /* now push it onto the file stack */
  115. ++Findex;
  116. if(Findex >= LIMIT_NESTED_INCLUDES) {
  117. Msg_Temp = GET_MSG (1014);
  118. SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, LIMIT_NESTED_INCLUDES);
  119. fatal(1014);
  120. }
  121. pF = &Fstack[Findex];
  122. p = (WCHAR *) MyAlloc((IO_BLOCK + PUSHBACK_BYTES) * sizeof(WCHAR));
  123. if (!p) {
  124. strcpy (Msg_Text, GET_MSG (1002));
  125. fatal(1002); /* no memory */
  126. return 0;
  127. }
  128. pF->fl_bufsiz = IO_BLOCK;
  129. pF->fl_currc = Current_char; /* previous file's current char */
  130. pF->fl_lineno = Linenumber; /* previous file's line number */
  131. pF->fl_file = Fp; /* the new file descriptor */
  132. pF->fl_buffer = p;
  133. pF->fl_numread = 0;
  134. pF->fl_seek = 0;
  135. pF->fl_fFileType = DetermineFileType (Fp);
  136. if (pF->fl_fFileType == DFT_FILE_IS_UNKNOWN) {
  137. Msg_Temp = GET_MSG (4413);
  138. SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, newname);
  139. warning (4413);
  140. pF->fl_fFileType = DFT_FILE_IS_8_BIT;
  141. }
  142. vfCurrFileType = pF->fl_fFileType;
  143. Current_char = p;
  144. io_eob(); /* fill the buffer */
  145. /*
  146. * Note that include filenames will live the entire compiland. This
  147. * puts the burden on the user with MANY include files. Any other
  148. * scheme takes space out of static data.
  149. * Notice also, that we save the previous filename in the new file's
  150. * fl_name.
  151. */
  152. pF->fl_name = pstrdup(Filename);
  153. wcsncpy(Filebuff, newname, sizeof(Filebuff) / sizeof(WCHAR));
  154. Linenumber = 0; /* do_newline() will increment to the first line */
  155. if(Eflag) {
  156. emit_line();
  157. // must manually write '\r' with '\n' when writing 16-bit strings
  158. myfwrite(L"\r\n", 2 * sizeof(WCHAR), 1, OUTPUTFILE); /* this line is inserted */
  159. }
  160. {
  161. defn_t d;
  162. int old_line = Linenumber;
  163. Linenumber = Findex;
  164. DEFN_IDENT(&d) = L"!";
  165. DEFN_TEXT(&d) = Reuse_Include;
  166. DEFN_NEXT(&d) = NULL;
  167. DEFN_NFORMALS(&d) = 0;
  168. DEFN_EXPANDING(&d) = FALSE;
  169. AfxOutputMacroDefn(&d);
  170. if (Findex > 0) {
  171. DEFN_IDENT(&d) = L"$";
  172. DEFN_TEXT(&d) = Filename;
  173. DEFN_NEXT(&d) = NULL;
  174. DEFN_NFORMALS(&d) = 0;
  175. DEFN_EXPANDING(&d) = FALSE;
  176. AfxOutputMacroDefn(&d);
  177. }
  178. Linenumber = old_line;
  179. }
  180. do_newline(); /* a new file may have preproc cmd as first line */
  181. return(TRUE);
  182. }
  183. /************************************************************************
  184. * FPOP - pop to a previous level of input stream
  185. *
  186. * ARGUMENTS - none
  187. *
  188. * RETURNS
  189. * TRUE if successful, FALSE if the stack is empty
  190. *
  191. * SIDE EFFECTS
  192. * - Linenumber is restored to the old files line number
  193. * - Filename is reset to the old filename
  194. * - frees storage allocated for filename
  195. *
  196. * DESCRIPTION
  197. * Pop the top of the file stack, restoring the previous input stream.
  198. *
  199. * AUTHOR - Ralph Ryan, Sept. 9, 1982
  200. *
  201. * MODIFICATIONS - none
  202. *
  203. ************************************************************************/
  204. WCHAR
  205. fpop(
  206. void
  207. )
  208. {
  209. int OldLine;
  210. defn_t DefType;
  211. if(Findex == -1) { /* no files left */
  212. return(FALSE);
  213. }
  214. if (Fp)
  215. fclose(Fp);
  216. OldLine = Linenumber;
  217. --Findex;
  218. Linenumber = Findex;
  219. DEFN_IDENT(&DefType) = L"!";
  220. DEFN_TEXT(&DefType) = L"";
  221. DEFN_NEXT(&DefType) = NULL;
  222. DEFN_NFORMALS(&DefType) = 0;
  223. DEFN_EXPANDING(&DefType) = FALSE;
  224. AfxOutputMacroDefn(&DefType);
  225. Findex++;
  226. Linenumber = OldLine;
  227. strappend(Filebuff,Fstack[Findex].fl_name);
  228. OldLine = Linenumber;
  229. Linenumber = (int)Fstack[Findex].fl_lineno;
  230. Current_char = Fstack[Findex].fl_currc;
  231. MyFree(Fstack[Findex].fl_buffer);
  232. if(--Findex < 0) { /* popped the last file */
  233. Linenumber = OldLine;
  234. return(FALSE);
  235. }
  236. Fp = Fstack[Findex].fl_file;
  237. vfCurrFileType = Fstack[Findex].fl_fFileType;
  238. if(Eflag) {
  239. // If the last file didn't end in a \r\n, the #line from emit_line could
  240. // end up in whatever data structure it ended in... Emit a dummy newline
  241. // just in case.
  242. myfwrite(L"\r\n", 2 * sizeof(WCHAR), 1, OUTPUTFILE); /* this line is inserted */
  243. emit_line();
  244. }
  245. return(TRUE);
  246. }
  247. /************************************************************************
  248. ** nested_include : searches the parentage list of the currently
  249. ** open files on the stack when a new include file is found.
  250. ** Input : ptr to include file name.
  251. ** Output : TRUE if the file was found, FALSE if not.
  252. *************************************************************************/
  253. int
  254. nested_include(
  255. void
  256. )
  257. {
  258. PWCHAR p_tmp1;
  259. PWCHAR p_file;
  260. PWCHAR p_slash;
  261. int tos;
  262. tos = Findex;
  263. p_file = Filename; /* always start with the current file */
  264. for(;;) {
  265. p_tmp1 = p_file;
  266. p_slash = NULL;
  267. while(*p_tmp1) { /* pt to end of filename, find trailing slash */
  268. if(wcschr(Path_chars, *p_tmp1)) {
  269. p_slash = p_tmp1;
  270. }
  271. p_tmp1++;
  272. }
  273. if(p_slash) {
  274. p_tmp1 = Reuse_W;
  275. while(p_file <= p_slash) { /* we want the trailing '/' */
  276. *p_tmp1++ = *p_file++; /* copy the parent directory */
  277. }
  278. p_file = yylval.yy_string.str_ptr;
  279. while((*p_tmp1++ = *p_file++)!=0) { /*append include file name */
  280. ; /* NULL */
  281. }
  282. } else {
  283. wcscpy(Reuse_W, yylval.yy_string.str_ptr);
  284. }
  285. if(newinput(Reuse_W,MAY_OPEN)) {
  286. return(TRUE);
  287. }
  288. if(tos <= 0) {
  289. break;
  290. }
  291. p_file = Fstack[tos--].fl_name;
  292. }
  293. return(FALSE);
  294. }
  295. /************************************************************************/
  296. /* esc_sequence() */
  297. /************************************************************************/
  298. PWCHAR
  299. esc_sequence(
  300. PWCHAR dest,
  301. PWCHAR name
  302. )
  303. {
  304. *dest = L'"';
  305. while((*++dest = *name) != 0) {
  306. if (CHARMAP(*name) == LX_EOS) {
  307. *++dest = L'\\';
  308. }
  309. name++;
  310. }
  311. *dest++ = L'"'; /* overwrite null */
  312. return( dest );
  313. }
  314. /************************************************************************/
  315. /* emit_line() */
  316. /************************************************************************/
  317. void
  318. emit_line(
  319. void
  320. )
  321. {
  322. PWCHAR p;
  323. swprintf(Reuse_W, L"#line %d ", Linenumber+1);
  324. myfwrite(Reuse_W, wcslen(Reuse_W) * sizeof(WCHAR), 1, OUTPUTFILE);
  325. p = esc_sequence(Reuse_W, Filename);
  326. myfwrite(Reuse_W, (size_t)(p - Reuse_W) * sizeof(WCHAR), 1, OUTPUTFILE);
  327. }
  328. /************************************************************************
  329. ** io_eob : handle getting the next block from a file.
  330. ** return TRUE if this is the real end of the buffer, FALSE if we have
  331. ** more to do.
  332. ************************************************************************/
  333. int
  334. io_eob(
  335. void
  336. )
  337. {
  338. int n;
  339. TEXT_TYPE p;
  340. static int dc;
  341. p = Fstack[Findex].fl_buffer;
  342. if((Current_char - (ptext_t)p) < Fstack[Findex].fl_numread) {
  343. /*
  344. ** haven't used all the chars from the buffer yet.
  345. ** (some clown has a null/cntl z embedded in his source file.)
  346. */
  347. if(PREVCH() == CONTROL_Z) { /* imbedded control z, real eof */
  348. UNGETCH();
  349. return(TRUE);
  350. }
  351. return(FALSE);
  352. }
  353. Current_char = p;
  354. //-
  355. //- The following section was added to support 16-bit resource files.
  356. //- It will just convert them to 8-bit files that the Resource Compiler
  357. //- can read. Here is the basic strategy used. An 8-bit file is
  358. //- read into the normal buffer and should be processed the old way.
  359. //- A 16-bit file is read into a wide character buffer identical to the
  360. //- normal 8-bit one. The entire contents are then copied to the 8-bit
  361. //- buffer and processed normally. The one exception to this is when
  362. //- a string literal is encountered. We then return to the 16-bit buffer
  363. //- to read the characters. These characters are written as backslashed
  364. //- escape characters inside an 8-bit string. (ex. "\x004c\x523f").
  365. //- I'll be the first person to admit that this is an ugly solution, but
  366. //- hey, we're Microsoft :-). 8-2-91 David Marsyla.
  367. //-
  368. if (Fstack[Findex].fl_fFileType == DFT_FILE_IS_8_BIT) {
  369. REG int i;
  370. REG PUCHAR lpb;
  371. PUCHAR Buf;
  372. Buf = (PUCHAR) MyAlloc(Fstack[Findex].fl_bufsiz + 1);
  373. if (Buf == NULL) {
  374. strcpy (Msg_Text, GET_MSG (1002));
  375. fatal(1002); /* no memory */
  376. }
  377. Fstack[Findex].fl_seek = fseek(Fp, 0, SEEK_CUR);
  378. n = fread (Buf, sizeof(char), Fstack[Findex].fl_bufsiz, Fp);
  379. //-
  380. //- Determine if the last byte is a DBCS lead byte
  381. //- if YES (i will be greater than n), backup one byte
  382. //-
  383. for (i = 0, lpb = Buf; i < n; i++, lpb++) {
  384. if (IsDBCSLeadByteEx(uiCodePage, *lpb)) {
  385. i++;
  386. lpb++;
  387. }
  388. }
  389. if (i > n) {
  390. fseek (Fp, -1, SEEK_CUR);
  391. n--;
  392. *(Buf + n) = 0;
  393. }
  394. //-
  395. //- Convert the 8-bit buffer to the 16-bit buffer.
  396. //-
  397. Fstack[Findex].fl_numread = MultiByteToWideChar (uiCodePage, MB_PRECOMPOSED,
  398. (LPCSTR) Buf, n, p, Fstack[Findex].fl_bufsiz);
  399. MyFree (Buf);
  400. } else {
  401. Fstack[Findex].fl_numread = n =
  402. fread (p, sizeof(WCHAR), Fstack[Findex].fl_bufsiz, Fp);
  403. //-
  404. //- If the file is in reversed format, swap the bytes.
  405. //-
  406. if (Fstack[Findex].fl_fFileType == DFT_FILE_IS_16_BIT_REV && n > 0) {
  407. WCHAR *pT = p;
  408. BYTE jLowNibble;
  409. BYTE jHighNibble;
  410. INT cNumWords = n;
  411. while (cNumWords--) {
  412. jLowNibble = (BYTE)(*pT & 0xFF);
  413. jHighNibble = (BYTE)((*pT >> 8) & 0xFF);
  414. *pT++ = (WCHAR)(jHighNibble | (jLowNibble << 8));
  415. }
  416. }
  417. }
  418. /*
  419. ** the total read counts the total read *and* used.
  420. */
  421. if (n != 0) { /* we read something */
  422. *(p + Fstack[Findex].fl_numread) = EOS_CHAR; /* sentinal at the end */
  423. return(FALSE); /* more to do */
  424. }
  425. *p = EOS_CHAR; /* read no chars */
  426. return(TRUE); /* real end of buffer */
  427. }
  428. /************************************************************************
  429. ** io_restart : restarts the current file with a new codepage
  430. ** Method: figure out where the current character came from
  431. ** using WideCharToMultiByte(...cch up to current char...)
  432. ** Note that this assumes that roundtrip conversion to/from
  433. ** Unicode results in the same # of characters out as in.
  434. ** fseek to the right place, then read a new buffer
  435. **
  436. ** Note that uiCodePage controls the seek, so it must
  437. ** remain set to the value used to do the translation
  438. ** from multi-byte to Unicode until after io_restart returns.
  439. **
  440. ************************************************************************/
  441. int
  442. io_restart(
  443. unsigned long cp
  444. )
  445. {
  446. int n;
  447. TEXT_TYPE p;
  448. // If it's a Unicode file, nothing to do, so just return.
  449. if (Fstack[Findex].fl_fFileType != DFT_FILE_IS_8_BIT)
  450. return TRUE;
  451. p = Fstack[Findex].fl_buffer;
  452. n = Fstack[Findex].fl_numread - (int)(Current_char - p);
  453. if (n != 0) {
  454. if (Fstack[Findex].fl_fFileType == DFT_FILE_IS_8_BIT) {
  455. n = WideCharToMultiByte(uiCodePage, 0, Current_char, n, NULL, 0, NULL, NULL);
  456. if (n == 0)
  457. return TRUE;
  458. } else
  459. n *= sizeof(WCHAR);
  460. fseek(Fp, -n, SEEK_CUR);
  461. }
  462. Fstack[Findex].fl_numread = 0;
  463. // io_eob will return true if we're at the end of the file.
  464. // this is an error for restart (it means there's nothing more
  465. // to do here (ie: #pragma codepage is the last line in the file).
  466. return !io_eob();
  467. }
  468. /************************************************************************
  469. ** p0_init : inits for prepocessing.
  470. ** Input : ptr to file name to use as input.
  471. ** ptr to LIST containing predefined values.
  472. ** ( -D's from cmd line )
  473. **
  474. ** Note : if "newinput" cannot open the file,
  475. ** it gives a fatal msg and exits.
  476. **
  477. ************************************************************************/
  478. void
  479. p0_init(
  480. WCHAR *p_fname,
  481. WCHAR *p_outname,
  482. LIST *p_defns,
  483. LIST *p_undefns
  484. )
  485. {
  486. REG WCHAR *p_dstr;
  487. REG WCHAR *p_eq;
  488. int ntop;
  489. SETCHARMAP(LX_FORMALMARK, LX_MACFORMAL);
  490. SETCHARMAP(LX_FORMALSTR, LX_STRFORMAL);
  491. SETCHARMAP(LX_FORMALCHAR, LX_CHARFORMAL);
  492. SETCHARMAP(LX_NOEXPANDMARK, LX_NOEXPAND);
  493. if(EXTENSION) {
  494. /*
  495. ** '$' is an identifier character under extensions.
  496. */
  497. SETCHARMAP(L'$', LX_ID);
  498. SETCONTMAP(L'$', LXC_ID);
  499. }
  500. for(ntop = p_defns->li_top; ntop < MAXLIST; ++ntop) {
  501. p_dstr = p_defns->li_defns[ntop];
  502. p_eq = Reuse_W;
  503. while ((*p_eq = *p_dstr++) != 0) { /* copy the name to Reuse_W */
  504. if(*p_eq == L'=') { /* we're told what the value is */
  505. break;
  506. }
  507. p_eq++;
  508. }
  509. if(*p_eq == L'=') {
  510. WCHAR *p_tmp;
  511. WCHAR *last_space = NULL;
  512. *p_eq = L'\0'; /* null the = */
  513. for(p_tmp = p_dstr; *p_tmp; p_tmp++) { /* find the end of it */
  514. if(iswspace(*p_tmp)) {
  515. if(last_space == NULL) {
  516. last_space = p_tmp;
  517. }
  518. } else {
  519. last_space = NULL;
  520. }
  521. }
  522. if(last_space != NULL) {
  523. *last_space = L'\0';
  524. }
  525. Reuse_W_hash = local_c_hash(Reuse_W);
  526. Reuse_W_length = wcslen(Reuse_W) + 1;
  527. if( *p_dstr ) { /* non-empty string */
  528. definstall(p_dstr, (wcslen(p_dstr) + 2), FROM_COMMAND);
  529. } else {
  530. definstall((WCHAR *)0, 0, 0);
  531. }
  532. } else {
  533. Reuse_W_hash = local_c_hash(Reuse_W);
  534. Reuse_W_length = wcslen(Reuse_W) + 1;
  535. definstall(L"1\000", 3, FROM_COMMAND); /* value of string is 1 */
  536. }
  537. }
  538. /* undefine */
  539. for(ntop = p_undefns->li_top; ntop < MAXLIST; ++ntop) {
  540. p_dstr = p_undefns->li_defns[ntop];
  541. p_eq = Reuse_W;
  542. while ((*p_eq = *p_dstr++) != 0) { /* copy the name to Reuse_W */
  543. if(*p_eq == L'=') { /* we're told what the value is */
  544. break;
  545. }
  546. p_eq++;
  547. }
  548. if(*p_eq == L'=') {
  549. WCHAR *p_tmp;
  550. WCHAR *last_space = NULL;
  551. *p_eq = L'\0'; /* null the = */
  552. for(p_tmp = p_dstr; *p_tmp; p_tmp++) { /* find the end of it */
  553. if(iswspace(*p_tmp)) {
  554. if(last_space == NULL) {
  555. last_space = p_tmp;
  556. }
  557. } else {
  558. last_space = NULL;
  559. }
  560. }
  561. if(last_space != NULL) {
  562. *last_space = L'\0';
  563. }
  564. Reuse_W_hash = local_c_hash(Reuse_W);
  565. Reuse_W_length = wcslen(Reuse_W) + 1;
  566. if( *p_dstr ) { /* non-empty string */
  567. undefine();
  568. } else {
  569. undefine();
  570. }
  571. } else {
  572. Reuse_W_hash = local_c_hash(Reuse_W);
  573. Reuse_W_length = wcslen(Reuse_W) + 1;
  574. undefine(); /* value of string is 1 */
  575. }
  576. }
  577. // Note: Always use the Ansi codepage here. uiCodePage may have been
  578. // modified by a codepage pragma in the source file.
  579. WideCharToMultiByte (GetACP(), 0, p_outname, -1, chBuf, sizeof(chBuf), NULL, NULL);
  580. if ((OUTPUTFILE = fopen (chBuf, "w+b")) == NULL) {
  581. Msg_Temp = GET_MSG (1023);
  582. SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, chBuf);
  583. fatal (1023);
  584. }
  585. newinput(p_fname,MUST_OPEN);
  586. }
  587. /************************************************************************
  588. ** p0_terminate : terminates prepocessing.
  589. **
  590. **
  591. ************************************************************************/
  592. void
  593. p0_terminate(
  594. void
  595. )
  596. {
  597. for ( ;fpop(); )
  598. ;
  599. if (OUTPUTFILE)
  600. fclose(OUTPUTFILE);
  601. }