domacro.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. /*
  2. * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
  3. * See the copyright notice in the ACK home directory, in the file "Copyright".
  4. */
  5. /* $Header$ */
  6. /* PREPROCESSOR: CONTROLLINE INTERPRETER */
  7. #include "interface.h"
  8. #include "arith.h"
  9. #include "LLlex.h"
  10. #include "Lpars.h"
  11. #include "debug.h"
  12. #include "idf.h"
  13. #include "input.h"
  14. #include "nopp.h"
  15. #include "lint.h"
  16. #ifndef NOPP
  17. #include "ifdepth.h"
  18. #include "botch_free.h"
  19. #include "nparams.h"
  20. #include "parbufsize.h"
  21. #include "textsize.h"
  22. #include "idfsize.h"
  23. #include "assert.h"
  24. #include <alloc.h>
  25. #include "class.h"
  26. #include "macro.h"
  27. #include "dbsymtab.h"
  28. #ifdef DBSYMTAB
  29. #include <stb.h>
  30. #include <em.h>
  31. int IncludeLevel = 0;
  32. extern char options[];
  33. #endif
  34. IMPORT char **inctable; /* list of include directories */
  35. IMPORT char *getwdir();
  36. PRIVATE char ifstack[IFDEPTH]; /* if-stack: the content of an entry is */
  37. /* 1 if a corresponding ELSE has been */
  38. /* encountered. */
  39. int nestlevel = -1;
  40. struct idf *
  41. GetIdentifier()
  42. {
  43. /* returns a pointer to the descriptor of the identifier that is
  44. read from the input stream. A null-pointer is returned if
  45. the input does not contain an identifier.
  46. The substitution of macros is disabled.
  47. */
  48. int tok;
  49. struct token tk;
  50. ReplaceMacros = 0;
  51. tok = GetToken(&tk);
  52. ReplaceMacros = 1;
  53. return tok == IDENTIFIER ? tk.tk_idf : (struct idf *)0;
  54. }
  55. /* domacro() is the control line interpreter. The '#' has already
  56. been read by the lexical analyzer by which domacro() is called.
  57. The token appearing directly after the '#' is obtained by calling
  58. the basic lexical analyzing function GetToken() and is interpreted
  59. to perform the action belonging to that token.
  60. An error message is produced when the token is not recognized,
  61. i.e. it is not one of "define" .. "undef" , integer or newline.
  62. */
  63. EXPORT
  64. domacro()
  65. {
  66. struct token tk; /* the token itself */
  67. EoiForNewline = 1;
  68. SkipEscNewline = 1;
  69. switch(GetToken(&tk)) { /* select control line action */
  70. case IDENTIFIER: /* is it a macro keyword? */
  71. switch (tk.tk_idf->id_resmac) {
  72. case K_DEFINE: /* "define" */
  73. do_define();
  74. break;
  75. case K_ELIF: /* "elif" */
  76. do_elif();
  77. break;
  78. case K_ELSE: /* "else" */
  79. do_else();
  80. break;
  81. case K_ENDIF: /* "endif" */
  82. do_endif();
  83. break;
  84. case K_IF: /* "if" */
  85. do_if();
  86. break;
  87. case K_IFDEF: /* "ifdef" */
  88. do_ifdef(1);
  89. break;
  90. case K_IFNDEF: /* "ifndef" */
  91. do_ifdef(0);
  92. break;
  93. case K_INCLUDE: /* "include" */
  94. do_include();
  95. break;
  96. case K_LINE: /* "line" */
  97. /* set LineNumber and FileName according to
  98. the arguments.
  99. */
  100. if (GetToken(&tk) != INTEGER) {
  101. lexerror("#line without linenumber");
  102. SkipRestOfLine();
  103. }
  104. else
  105. do_line((unsigned int)tk.tk_ival);
  106. break;
  107. case K_UNDEF: /* "undef" */
  108. do_undef();
  109. break;
  110. case K_PRAGMA: /* "pragma" */
  111. /* ignore for now
  112. */
  113. SkipRestOfLine();
  114. break;
  115. default:
  116. /* invalid word seen after the '#' */
  117. lexerror("%s: unknown control", tk.tk_idf->id_text);
  118. SkipRestOfLine();
  119. }
  120. break;
  121. case INTEGER: /* # <integer> [<filespecifier>]? */
  122. do_line((unsigned int)tk.tk_ival);
  123. break;
  124. case EOI: /* only `#' on this line: do nothing, ignore */
  125. break;
  126. default: /* invalid token following '#' */
  127. lexerror("illegal # line");
  128. SkipRestOfLine();
  129. }
  130. EoiForNewline = 0;
  131. SkipEscNewline = 0;
  132. }
  133. #ifdef LINT
  134. int lint_skip_comment;
  135. #endif
  136. PRIVATE
  137. skip_block(to_endif)
  138. {
  139. /* skip_block() skips the input from
  140. 1) a false #if, #ifdef, #ifndef or #elif until the
  141. corresponding #elif (resulting in true), #else or
  142. #endif is read.
  143. 2) a #else corresponding to a true #if, #ifdef,
  144. #ifndef or #elif until the corresponding #endif is
  145. seen.
  146. */
  147. register int ch;
  148. register int skiplevel = nestlevel; /* current nesting level */
  149. struct token tk;
  150. #ifdef LINT
  151. lint_skip_comment++;
  152. #endif
  153. NoUnstack++;
  154. for (;;) {
  155. LoadChar(ch); /* read first character after newline */
  156. if (ch != '#') {
  157. if (ch == EOI) {
  158. NoUnstack--;
  159. #ifdef LINT
  160. lint_skip_comment--;
  161. #endif
  162. return;
  163. }
  164. SkipRestOfLine();
  165. continue;
  166. }
  167. if (GetToken(&tk) != IDENTIFIER) {
  168. SkipRestOfLine();
  169. continue;
  170. }
  171. /* an IDENTIFIER: look for #if, #ifdef and #ifndef
  172. without interpreting them.
  173. Interpret #else, #elif and #endif if they occur
  174. on the same level.
  175. */
  176. switch(tk.tk_idf->id_resmac) {
  177. default:
  178. SkipRestOfLine();
  179. break;
  180. case K_IF:
  181. case K_IFDEF:
  182. case K_IFNDEF:
  183. push_if();
  184. SkipRestOfLine();
  185. break;
  186. case K_ELIF:
  187. if (ifstack[nestlevel])
  188. lexwarning("#elif without corresponding #if");
  189. if (! to_endif && nestlevel == skiplevel) {
  190. nestlevel--;
  191. push_if();
  192. if (ifexpr()) {
  193. NoUnstack--;
  194. #ifdef LINT
  195. lint_skip_comment--;
  196. #endif
  197. return;
  198. }
  199. }
  200. else SkipRestOfLine();
  201. break;
  202. case K_ELSE:
  203. if (ifstack[nestlevel])
  204. lexwarning("#else without corresponding #if");
  205. SkipRestOfLine();
  206. if (! to_endif) {
  207. ++(ifstack[nestlevel]);
  208. if (nestlevel == skiplevel) {
  209. NoUnstack--;
  210. #ifdef LINT
  211. lint_skip_comment--;
  212. #endif
  213. return;
  214. }
  215. }
  216. break;
  217. case K_ENDIF:
  218. ASSERT(nestlevel > nestlow);
  219. SkipRestOfLine();
  220. if (nestlevel == skiplevel) {
  221. nestlevel--;
  222. NoUnstack--;
  223. #ifdef LINT
  224. lint_skip_comment--;
  225. #endif
  226. return;
  227. }
  228. nestlevel--;
  229. break;
  230. }
  231. }
  232. }
  233. PRIVATE
  234. ifexpr()
  235. {
  236. /* ifexpr() returns whether the restricted constant
  237. expression following #if or #elif evaluates to true. This
  238. is done by calling the LLgen generated subparser for
  239. constant expressions. The result of this expression will
  240. be given in the extern long variable "ifval".
  241. */
  242. IMPORT arith ifval;
  243. int errors = err_occurred;
  244. ifval = (arith)0;
  245. AccDefined = 1;
  246. UnknownIdIsZero = 1;
  247. PushLex(); /* NEW parser */
  248. If_expr(); /* invoke constant expression parser */
  249. PopLex(); /* OLD parser */
  250. AccDefined = 0;
  251. UnknownIdIsZero = 0;
  252. return (errors == err_occurred) && (ifval != (arith)0);
  253. }
  254. PRIVATE
  255. do_include()
  256. {
  257. /* do_include() performs the inclusion of a file.
  258. */
  259. char *filenm;
  260. char *result;
  261. int tok;
  262. struct token tk;
  263. AccFileSpecifier = 1;
  264. if (((tok = GetToken(&tk)) == FILESPECIFIER) || tok == STRING)
  265. filenm = tk.tk_bts;
  266. else {
  267. lexerror("bad include syntax");
  268. filenm = (char *)0;
  269. }
  270. AccFileSpecifier = 0;
  271. SkipRestOfLine();
  272. inctable[0] = WorkingDir;
  273. if (filenm) {
  274. if (!InsertFile(filenm, &inctable[tok==FILESPECIFIER],&result)){
  275. fatal("cannot open include file \"%s\"", filenm);
  276. }
  277. else {
  278. add_dependency(result);
  279. WorkingDir = getwdir(result);
  280. File_Inserted = 1;
  281. FileName = result;
  282. LineNumber = 0;
  283. nestlow = nestlevel;
  284. #ifdef DBSYMTAB
  285. IncludeLevel++;
  286. if (options['g']) {
  287. C_ms_stb_cst(FileName, N_BINCL, 0, (arith) 0);
  288. }
  289. #endif /* DBSYMTAB */
  290. }
  291. }
  292. }
  293. PRIVATE
  294. do_define()
  295. {
  296. /* do_define() interprets a #define control line.
  297. */
  298. struct idf *id; /* the #defined identifier's descriptor */
  299. int nformals = -1; /* keep track of the number of formals */
  300. char *formals[NPARAMS]; /* pointers to the names of the formals */
  301. char parbuf[PARBUFSIZE]; /* names of formals */
  302. char *repl_text; /* start of the replacement text */
  303. int length; /* length of the replacement text */
  304. register ch;
  305. char *get_text();
  306. /* read the #defined macro's name */
  307. if (!(id = GetIdentifier())) {
  308. lexerror("#define: illegal macro name");
  309. SkipRestOfLine();
  310. return;
  311. }
  312. /* there is a formal parameter list if the identifier is
  313. followed immediately by a '('.
  314. */
  315. LoadChar(ch);
  316. if (ch == '(') {
  317. if ((nformals = getparams(formals, parbuf)) == -1) {
  318. SkipRestOfLine();
  319. return; /* an error occurred */
  320. }
  321. LoadChar(ch);
  322. }
  323. /* read the replacement text if there is any */
  324. ch = skipspaces(ch,0); /* find first character of the text */
  325. ASSERT(ch != EOI);
  326. if (class(ch) == STNL) {
  327. /* Treat `#define something' as `#define something ""'
  328. */
  329. repl_text = "";
  330. length = 0;
  331. }
  332. else {
  333. PushBack();
  334. repl_text = get_text((nformals > 0) ? formals : 0, &length);
  335. }
  336. macro_def(id, repl_text, nformals, length, NOFLAG);
  337. LineNumber++;
  338. }
  339. PRIVATE
  340. push_if()
  341. {
  342. if (nestlevel >= IFDEPTH)
  343. fatal("too many nested #if/#ifdef/#ifndef");
  344. else
  345. ifstack[++nestlevel] = 0;
  346. }
  347. PRIVATE
  348. do_elif()
  349. {
  350. if (nestlevel <= nestlow || (ifstack[nestlevel])) {
  351. lexerror("#elif without corresponding #if");
  352. SkipRestOfLine();
  353. }
  354. else { /* restart at this level as if a #if is detected. */
  355. nestlevel--;
  356. push_if();
  357. skip_block(1);
  358. }
  359. }
  360. PRIVATE
  361. do_else()
  362. {
  363. SkipRestOfLine();
  364. if (nestlevel <= nestlow || (ifstack[nestlevel]))
  365. lexerror("#else without corresponding #if");
  366. else { /* mark this level as else-d */
  367. ++(ifstack[nestlevel]);
  368. skip_block(1);
  369. }
  370. }
  371. PRIVATE
  372. do_endif()
  373. {
  374. SkipRestOfLine();
  375. if (nestlevel <= nestlow) {
  376. lexerror("#endif without corresponding #if");
  377. }
  378. else nestlevel--;
  379. }
  380. PRIVATE
  381. do_if()
  382. {
  383. push_if();
  384. if (!ifexpr()) /* a false #if/#elif expression */
  385. skip_block(0);
  386. }
  387. PRIVATE
  388. do_ifdef(how)
  389. {
  390. register struct idf *id;
  391. /* how == 1 : ifdef; how == 0 : ifndef
  392. */
  393. push_if();
  394. if (!(id = GetIdentifier()))
  395. lexerror("illegal #ifdef construction");
  396. /* The next test is a shorthand for:
  397. (how && !id->id_macro) || (!how && id->id_macro)
  398. */
  399. if (how ^ (id && id->id_macro != 0))
  400. skip_block(0);
  401. else
  402. SkipRestOfLine();
  403. }
  404. PRIVATE
  405. do_undef()
  406. {
  407. register struct idf *id;
  408. /* Forget a macro definition. */
  409. if (id = GetIdentifier()) {
  410. if (id->id_macro) { /* forget the macro */
  411. free_macro(id->id_macro);
  412. id->id_macro = (struct macro *) 0;
  413. } /* else: don't complain */
  414. }
  415. else
  416. lexerror("illegal #undef construction");
  417. SkipRestOfLine();
  418. }
  419. PRIVATE int
  420. getparams(buf, parbuf)
  421. char *buf[];
  422. char parbuf[];
  423. {
  424. /* getparams() reads the formal parameter list of a macro
  425. definition.
  426. The number of parameters is returned.
  427. As a formal parameter list is expected when calling this
  428. routine, -1 is returned if an error is detected, for
  429. example:
  430. #define one(1), where 1 is not an identifier.
  431. Note that the '(' has already been eaten.
  432. The names of the formal parameters are stored into parbuf.
  433. */
  434. register char **pbuf = &buf[0];
  435. register int c;
  436. register char *ptr = &parbuf[0];
  437. register char **pbuf2;
  438. LoadChar(c);
  439. c = skipspaces(c,0);
  440. if (c == ')') { /* no parameters: #define name() */
  441. *pbuf = (char *) 0;
  442. return 0;
  443. }
  444. for (;;) { /* eat the formal parameter list */
  445. if (class(c) != STIDF) { /* not an identifier */
  446. lexerror("#define: bad formal parameter");
  447. return -1;
  448. }
  449. *pbuf = ptr; /* name of the formal */
  450. *ptr++ = c;
  451. if (ptr >= &parbuf[PARBUFSIZE])
  452. fatal("formal parameter buffer overflow");
  453. do { /* eat the identifier name */
  454. LoadChar(c);
  455. *ptr++ = c;
  456. if (ptr >= &parbuf[PARBUFSIZE])
  457. fatal("formal parameter buffer overflow");
  458. } while (in_idf(c));
  459. *(ptr - 1) = '\0'; /* mark end of the name */
  460. /* Check if this formal parameter is already used.
  461. Usually, macros do not have many parameters, so ...
  462. */
  463. for (pbuf2 = pbuf - 1; pbuf2 >= &buf[0]; pbuf2--) {
  464. if (!strcmp(*pbuf2, *pbuf)) {
  465. warning("formal parameter \"%s\" already used",
  466. *pbuf);
  467. }
  468. }
  469. pbuf++;
  470. c = skipspaces(c,0);
  471. if (c == ')') { /* end of the formal parameter list */
  472. *pbuf = (char *) 0;
  473. return pbuf - buf;
  474. }
  475. if (c != ',') {
  476. lexerror("#define: bad formal parameter list");
  477. return -1;
  478. }
  479. LoadChar(c);
  480. c = skipspaces(c,0);
  481. }
  482. /*NOTREACHED*/
  483. }
  484. EXPORT
  485. macro_def(id, text, nformals, length, flags)
  486. register struct idf *id;
  487. char *text;
  488. {
  489. register struct macro *newdef = id->id_macro;
  490. /* macro_def() puts the contents and information of a macro
  491. definition into a structure and stores it into the symbol
  492. table entry belonging to the name of the macro.
  493. A warning is given if the definition overwrites another.
  494. */
  495. if (newdef) { /* is there a redefinition? */
  496. if (macroeq(newdef->mc_text, text))
  497. return;
  498. lexwarning("redefine \"%s\"", id->id_text);
  499. }
  500. else
  501. id->id_macro = newdef = new_macro();
  502. newdef->mc_text = text; /* replacement text */
  503. newdef->mc_nps = nformals; /* nr of formals */
  504. newdef->mc_length = length; /* length of repl. text */
  505. newdef->mc_flag = flags; /* special flags */
  506. newdef->mc_count = 0;
  507. }
  508. PRIVATE int
  509. find_name(nm, index)
  510. char *nm, *index[];
  511. {
  512. /* find_name() returns the index of "nm" in the namelist
  513. "index" if it can be found there. 0 is returned if it is
  514. not there.
  515. */
  516. register char **ip = &index[0];
  517. while (*ip)
  518. if (strcmp(nm, *ip++) == 0)
  519. return ip - &index[0];
  520. /* arrived here, nm is not in the name list. */
  521. return 0;
  522. }
  523. PRIVATE char *
  524. get_text(formals, length)
  525. char *formals[];
  526. int *length;
  527. {
  528. /* get_text() copies the replacement text of a macro
  529. definition with zero, one or more parameters, thereby
  530. substituting each formal parameter by a special character
  531. (non-ascii: 0200 & (order-number in the formal parameter
  532. list)) in order to substitute this character later by the
  533. actual parameter. The replacement text is copied into
  534. itself because the copied text will contain fewer or the
  535. same amount of characters. The length of the replacement
  536. text is returned.
  537. Implementation:
  538. finite automaton : we are only interested in
  539. identifiers, because they might be replaced by some actual
  540. parameter. Other tokens will not be seen as such.
  541. */
  542. register int c;
  543. register int text_size;
  544. char *text = Malloc(text_size = ITEXTSIZE);
  545. register int pos = 0;
  546. LoadChar(c);
  547. while ((c != EOI) && (class(c) != STNL)) {
  548. if (c == '\\') { /* check for "\\\n" */
  549. LoadChar(c);
  550. if (c == '\n') {
  551. /* More than one line is used for the
  552. replacement text.
  553. Replace "\\\n" by " ".
  554. */
  555. text[pos++] = ' ';
  556. ++LineNumber;
  557. LoadChar(c);
  558. }
  559. else
  560. text[pos++] = '\\';
  561. if (pos == text_size)
  562. text = Srealloc(text, text_size += RTEXTSIZE);
  563. }
  564. else
  565. if ( c == '/') {
  566. LoadChar(c);
  567. if (c == '*') {
  568. skipcomment();
  569. /* text[pos++] = ' '; ??? Why ??? */
  570. LoadChar(c);
  571. }
  572. else
  573. text[pos++] = '/';
  574. if (pos == text_size)
  575. text = Srealloc(text, text_size += RTEXTSIZE);
  576. }
  577. else
  578. if (formals && class(c) == STIDF) {
  579. char id_buf[IDFSIZE + 1];
  580. register id_size = 0;
  581. register n;
  582. /* read identifier: it may be a formal parameter */
  583. id_buf[id_size++] = c;
  584. do {
  585. LoadChar(c);
  586. if (id_size <= IDFSIZE)
  587. id_buf[id_size++] = c;
  588. } while (in_idf(c));
  589. id_buf[--id_size] = '\0';
  590. if (n = find_name(id_buf, formals)) {
  591. /* construct the formal parameter mark */
  592. text[pos++] = FORMALP | (char) n;
  593. if (pos == text_size)
  594. text = Srealloc(text,
  595. text_size += RTEXTSIZE);
  596. }
  597. else {
  598. register char *ptr = &id_buf[0];
  599. while (pos + id_size >= text_size)
  600. text = Srealloc(text,
  601. text_size += RTEXTSIZE);
  602. while (text[pos++] = *ptr++) ;
  603. pos--;
  604. }
  605. }
  606. else {
  607. text[pos++] = c;
  608. if (pos == text_size)
  609. text = Srealloc(text, text_size += RTEXTSIZE);
  610. LoadChar(c);
  611. }
  612. }
  613. text[pos++] = '\0';
  614. *length = pos - 1;
  615. return text;
  616. }
  617. #define BLANK(ch) ((ch == ' ') || (ch == '\t'))
  618. /* macroeq() decides whether two macro replacement texts are
  619. identical. This version compares the texts, which occur
  620. as strings, without taking care of the leading and trailing
  621. blanks (spaces and tabs).
  622. */
  623. PRIVATE
  624. macroeq(s, t)
  625. register char *s, *t;
  626. {
  627. /* skip leading spaces */
  628. while (BLANK(*s)) s++;
  629. while (BLANK(*t)) t++;
  630. /* first non-blank encountered in both strings */
  631. /* The actual comparison loop: */
  632. while (*s && *s == *t)
  633. s++, t++;
  634. /* two cases are possible when arrived here: */
  635. if (*s == '\0') { /* *s == '\0' */
  636. while (BLANK(*t)) t++;
  637. return *t == '\0';
  638. }
  639. else { /* *s != *t */
  640. while (BLANK(*s)) s++;
  641. while (BLANK(*t)) t++;
  642. return (*s == '\0') && (*t == '\0');
  643. }
  644. }
  645. #else /* NOPP */
  646. EXPORT
  647. domacro()
  648. {
  649. int tok;
  650. struct token tk;
  651. EoiForNewline = 1;
  652. SkipEscNewline = 1;
  653. if ((tok = GetToken(&tk)) == IDENTIFIER) {
  654. if (strcmp(tk.tk_idf->id_text, "line") != 0) {
  655. error("illegal # line");
  656. SkipRestOfLine();
  657. return;
  658. }
  659. tok = GetToken(&tk);
  660. }
  661. if (tok != INTEGER) {
  662. error("illegal # line");
  663. SkipRestOfLine();
  664. return;
  665. }
  666. do_line((unsigned int) tk.tk_ival);
  667. EoiForNewline = 0;
  668. SkipEscNewline = 0;
  669. }
  670. #endif /* NOPP */
  671. PRIVATE
  672. SkipRestOfLine()
  673. {
  674. /* we do a PushBack because we don't want to skip the next line
  675. if the last character was a newline
  676. */
  677. PushBack();
  678. skipline();
  679. }
  680. PRIVATE
  681. do_line(l)
  682. unsigned int l;
  683. {
  684. struct token tk;
  685. int t = GetToken(&tk);
  686. SkipRestOfLine();
  687. LineNumber = l; /* the number of the next input line */
  688. if (t == STRING) { /* is there a filespecifier? */
  689. #ifdef DBSYMTAB
  690. if (options['g'] && strcmp(FileName, tk.tk_bts) != 0) {
  691. C_ms_std(tk.tk_bts, N_SOL, 0);
  692. }
  693. #endif /* DBSYMTAB */
  694. FileName = tk.tk_bts;
  695. }
  696. }