replace.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  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. /* M A C R O R E P L A C E M E N T */
  7. #include "pathlength.h"
  8. #include "strsize.h"
  9. #include "nparams.h"
  10. #include "idfsize.h"
  11. #include "numsize.h"
  12. #include <alloc.h>
  13. #include "idf.h"
  14. #include "input.h"
  15. #include "macro.h"
  16. #include "arith.h"
  17. #include "LLlex.h"
  18. #include "class.h"
  19. #include <assert.h>
  20. #include "lapbuf.h"
  21. #include "argbuf.h"
  22. #include "replace.h"
  23. extern char *GetIdentifier();
  24. extern int InputLevel;
  25. struct repl *ReplaceList; /* list of currently active macros */
  26. int
  27. replace(idf)
  28. register struct idf *idf;
  29. {
  30. /* replace is called by the lexical analyzer to perform
  31. macro replacement. The routine actualy functions as a
  32. higher interface to the real thing: expand_macro().
  33. */
  34. struct repl *repl;
  35. if (!(idf->id_macro)) return 0;
  36. if (idf->id_macro->mc_flag & NOREPLACE)
  37. return 0;
  38. repl = new_repl();
  39. repl->r_ptr = repl->r_text;
  40. repl->r_args = new_args();
  41. repl->r_idf = idf;
  42. if (!expand_macro(repl, idf))
  43. return 0;
  44. InputLevel++;
  45. InsertText(repl->r_text, repl->r_ptr - repl->r_text);
  46. idf->id_macro->mc_flag |= NOREPLACE;
  47. repl->r_level = InputLevel;
  48. repl->next = ReplaceList;
  49. ReplaceList = repl;
  50. return 1;
  51. }
  52. unstackrepl()
  53. {
  54. Unstacked++;
  55. }
  56. EnableMacros()
  57. {
  58. register struct repl *r = ReplaceList, *prev = 0;
  59. assert(Unstacked > 0);
  60. while(r) {
  61. struct repl *nxt = r->next;
  62. if (r->r_level > InputLevel) {
  63. r->r_idf->id_macro->mc_flag &= ~NOREPLACE;
  64. if (!prev) ReplaceList = nxt;
  65. else prev->next = nxt;
  66. free_args(r->r_args);
  67. free_repl(r);
  68. }
  69. else prev = r;
  70. r = nxt;
  71. }
  72. Unstacked = 0;
  73. }
  74. expand_macro(repl, idf)
  75. register struct repl *repl;
  76. register struct idf *idf;
  77. {
  78. /* expand_macro() does the actual macro replacement.
  79. "idf" is a description of the identifier which
  80. caused the replacement.
  81. If the identifier represents a function-like macro
  82. call, the number of actual parameters is checked
  83. against the number of formal parameters. Note that
  84. in ANSI C the parameters are expanded first;
  85. this is done by calling getactuals().
  86. When the possible parameters are expanded, the replace-
  87. ment list associated with "idf" is expanded.
  88. expand_macro() returns 1 if the replacement succeeded
  89. and 0 if some error occurred.
  90. A special case is "defined". This acts as a unary operator
  91. on a single, unexpanded identifier, which may be surrounded
  92. by parenthesis. The function expand_defined() handles this.
  93. */
  94. register struct macro *mac = idf->id_macro;
  95. struct args *args = repl->r_args;
  96. register int ch;
  97. if (mac->mc_nps != -1) { /* with parameter list */
  98. if (mac->mc_flag & FUNC) {
  99. /* the following assertion won't compile:
  100. assert(!strcmp("defined", idf->id_text));
  101. */
  102. if (!AccDefined) return 0;
  103. expand_defined(repl);
  104. return 1;
  105. }
  106. ch = GetChar();
  107. ch = skipspaces(ch,1);
  108. if (ch != '(') { /* no replacement if no () */
  109. UnGetChar();
  110. return 0;
  111. } else
  112. getactuals(repl, idf);
  113. }
  114. if (mac->mc_flag & FUNC) /* this macro leads to special action */
  115. macro_func(idf);
  116. macro2buffer(repl, idf, args);
  117. /* According to the ANSI definition:
  118. #define a +
  119. a+b; --> + + b ;
  120. 'a' must be substituded, but the result should be
  121. three tokens: + + ID. Because this preprocessor is
  122. character based, we have a problem.
  123. For now: just insert a space after all tokens,
  124. until ANSI fixes this flaw.
  125. ^^^^^^^^^^^^^^^^^^^^^^^^^^ tsk tsk tsk
  126. */
  127. if (*repl->r_ptr != TOKSEP) *repl->r_ptr++ = TOKSEP;
  128. *repl->r_ptr = '\0';
  129. return 1;
  130. }
  131. expand_defined(repl)
  132. register struct repl *repl;
  133. {
  134. register int ch = GetChar();
  135. struct idf *id;
  136. char *str;
  137. int parens = 0;
  138. ch = skipspaces(ch, 0);
  139. if (ch == '(') {
  140. parens++;
  141. ch = GetChar();
  142. ch = skipspaces(ch, 0);
  143. }
  144. if ((class(ch) != STIDF) && (class(ch) != STELL)) {
  145. error("identifier missing");
  146. if (parens && ch != ')') error(") missing");
  147. if (!parens || ch != ')') UnGetChar();
  148. *repl->r_ptr++ = '0';
  149. *repl->r_ptr = '\0';
  150. return;
  151. }
  152. UnGetChar();
  153. str = GetIdentifier(0);
  154. if (str)
  155. id = str2idf(str, 0);
  156. else id = 0;
  157. assert(id || class(ch) == STELL);
  158. ch = GetChar();
  159. ch = skipspaces(ch, 0);
  160. if (parens && ch != ')') error(") missing");
  161. if (!parens || ch != ')') UnGetChar();
  162. *repl->r_ptr++ = (id && id->id_macro) ? '1' : '0';
  163. *repl->r_ptr = '\0';
  164. }
  165. getactuals(repl, idf)
  166. struct repl *repl;
  167. register struct idf *idf;
  168. {
  169. /* Get the actual parameters from the input stream.
  170. The hard part is done by actual(), only comma's and
  171. other syntactic trivialities are checked here.
  172. */
  173. register struct args *args = repl->r_args;
  174. register int nps = idf->id_macro->mc_nps;
  175. register int argcnt;
  176. register int ch;
  177. argcnt = 0;
  178. args->a_expvec[0] = args->a_expptr = &args->a_expbuf[0];
  179. args->a_rawvec[0] = args->a_rawptr = &args->a_rawbuf[0];
  180. if ((ch = GetChar()) != ')') {
  181. UnGetChar();
  182. while ((ch = actual(repl)) != ')' ) {
  183. if (ch != ',') {
  184. error("illegal macro call");
  185. return;
  186. }
  187. stash(repl, '\0', 1);
  188. ++argcnt;
  189. args->a_expvec[argcnt] = args->a_expptr;
  190. args->a_rawvec[argcnt] = args->a_rawptr;
  191. if (argcnt == STDC_NPARAMS)
  192. strict("number of parameters exceeds ANSI standard");
  193. if (argcnt >= NPARAMS)
  194. fatal("argument vector overflow");
  195. }
  196. stash(repl, '\0', 1);
  197. ++argcnt;
  198. }
  199. if (argcnt < nps)
  200. error("too few macro arguments");
  201. else if (argcnt > nps)
  202. error("too many macro arguments");
  203. }
  204. saveraw(repl)
  205. struct repl *repl;
  206. {
  207. register struct repl *nrepl = ReplaceList;
  208. register struct args *ap = nrepl->r_args;
  209. struct args *args = repl->r_args;
  210. register char *p;
  211. /* stash identifier name */
  212. for (p = nrepl->r_idf->id_text; *p != '\0'; p++)
  213. *args->a_rawptr++ = *p;
  214. /* The following code deals with expanded function
  215. like macro calls. It makes the following code
  216. work:
  217. #define def(a,b) x(a,b)
  218. #define glue(a,b) a ## b
  219. glue(abc,def(a,b))
  220. Results in:
  221. abcdef(a,b);
  222. */
  223. if (ap->a_rawvec[0]) {
  224. /* stash arguments */
  225. register int i;
  226. *args->a_rawptr++ = '(';
  227. for (i = 0; ap->a_rawvec[i] != (char *)0; i++) {
  228. for (p = ap->a_rawvec[i]; *p != '\0'; p++)
  229. *args->a_rawptr++ = *p;
  230. *args->a_rawptr++ = ',';
  231. }
  232. *(args->a_rawptr-1) = ')'; /* delete last ',' */
  233. }
  234. }
  235. int
  236. actual(repl)
  237. struct repl *repl;
  238. {
  239. /* This routine deals with the scanning of an actual parameter.
  240. It keeps in account the opening and closing brackets,
  241. preprocessor numbers, strings and character constants.
  242. */
  243. register int ch;
  244. register int level = 0, nostashraw = 0;
  245. while (1) {
  246. ch = GetChar();
  247. if (Unstacked) {
  248. nostashraw -= Unstacked;
  249. if (nostashraw < 0) nostashraw = 0;
  250. EnableMacros();
  251. }
  252. if (class(ch) == STIDF || class(ch) == STELL) {
  253. /* Scan a preprocessor identifier token. If the
  254. token is a macro, it is expanded first.
  255. */
  256. char buf[(IDFSIZE > NUMSIZE ? IDFSIZE : NUMSIZE) + 1];
  257. register char *p = buf;
  258. register struct idf *idef;
  259. register int pos = -1;
  260. extern int idfsize;
  261. int NoExpandMacro;
  262. if (ch == NOEXPM) {
  263. NoExpandMacro= 1;
  264. ch = GetChar();
  265. } else NoExpandMacro = 0;
  266. do {
  267. if (++pos < idfsize) {
  268. *p++ = ch;
  269. }
  270. ch = GetChar();
  271. } while (in_idf(ch));
  272. *p++ = '\0';
  273. UnGetChar();
  274. /* When the identifier has an associated macro
  275. replacement list, it's expanded.
  276. */
  277. idef = findidf(buf);
  278. if (!idef || NoExpandMacro || !replace(idef)) {
  279. if (NoExpandMacro
  280. || (idef && idef->id_macro
  281. && (idef->id_macro->mc_flag & NOREPLACE)))
  282. stash(repl, NOEXPM, !nostashraw);
  283. for (p = buf; *p != '\0'; p++)
  284. stash(repl, *p, !nostashraw);
  285. } else {
  286. if (!nostashraw) saveraw(repl);
  287. nostashraw++;
  288. }
  289. } else if (class(ch) == STNUM) {
  290. /* a preprocessing number has the following
  291. regular expression:
  292. [0-9|"."[0-9]]{[0-9"."a-zA-Z_]|{[Ee][+-]}}*
  293. */
  294. stash(repl, ch, !nostashraw);
  295. if (ch == '.') {
  296. ch = GetChar();
  297. if (class(ch) != STNUM) {
  298. UnGetChar();
  299. continue;
  300. }
  301. else stash(repl, ch, !nostashraw);
  302. }
  303. ch = GetChar();
  304. while (in_idf(ch) || ch == '.') {
  305. stash(repl, ch, !nostashraw);
  306. if ((ch = GetChar()) == 'e' || ch == 'E') {
  307. stash(repl, ch, !nostashraw);
  308. ch = GetChar();
  309. if (ch == '+' || ch == '-') {
  310. stash(repl, ch, !nostashraw);
  311. ch = GetChar();
  312. }
  313. }
  314. }
  315. UnGetChar();
  316. } else if (ch == '(' || ch == '[' || ch == '{') {
  317. /* a comma may occur within these constructions ???
  318. */
  319. level++;
  320. stash(repl, ch, !nostashraw);
  321. } else if (ch == ')' || ch == ']' || ch == '}') {
  322. level--;
  323. /* clossing parenthesis of macro call */
  324. if (ch == ')' && level < 0)
  325. return ')';
  326. stash(repl, ch, !nostashraw);
  327. } else if (ch == ',') {
  328. if (level <= 0) { /* comma separator for next argument */
  329. if (level)
  330. error("unbalanced parenthesis");
  331. if (!nostashraw)
  332. return ','; /* ??? */
  333. }
  334. stash(repl, ch, !nostashraw);
  335. } else if (ch == '\n') {
  336. /* newlines are accepted as white spaces */
  337. LineNumber++;
  338. while ((ch = GetChar()), class(ch) == STSKIP)
  339. /* EMPTY */;
  340. /* This piece of code needs some explanation:
  341. consider the call of a macro defined as:
  342. #define sum(a,b) (a+b)
  343. in the following form:
  344. sum(
  345. #include phone_number
  346. ,2);
  347. in which case the include must be handled
  348. interpreted as such.
  349. */
  350. if (ch == '#')
  351. domacro();
  352. else if (ch == EOI) {
  353. error("unterminated macro call");
  354. return ')';
  355. }
  356. UnGetChar();
  357. stash(repl, ' ', !nostashraw);
  358. } else if (ch == '/') {
  359. /* comments are treated as one white space token */
  360. if ((ch = GetChar()) == '*' && !InputLevel) {
  361. skipcomment();
  362. stash(repl, ' ', !nostashraw);
  363. } else {
  364. UnGetChar();
  365. stash(repl, '/', !nostashraw);
  366. }
  367. } else if (ch == '\'' || ch == '"') {
  368. /* Strings are considered as ONE token, thus no
  369. replacement within strings.
  370. */
  371. register int match = ch;
  372. stash(repl, ch, !nostashraw);
  373. while ((ch = GetChar()) != EOI) {
  374. if (ch == match)
  375. break;
  376. if (ch == '\\') {
  377. stash(repl, ch, !nostashraw);
  378. ch = GetChar();
  379. } else if (ch == '\n') {
  380. error("newline in string");
  381. LineNumber++;
  382. stash(repl, match, !nostashraw);
  383. break;
  384. }
  385. stash(repl, ch, !nostashraw);
  386. }
  387. if (ch != match) {
  388. error("unterminated macro call");
  389. return ')';
  390. }
  391. stash(repl, ch, !nostashraw);
  392. } else
  393. stash(repl, ch, !nostashraw);
  394. }
  395. }
  396. macro_func(idef)
  397. register struct idf *idef;
  398. {
  399. /* macro_func() performs the special actions needed with some
  400. macros. These macros are __FILE__ and __LINE__ which
  401. replacement texts must be evaluated at the time they are
  402. used.
  403. */
  404. register struct macro *mac = idef->id_macro;
  405. static char FilNamBuf[PATHLENGTH];
  406. char *long2str();
  407. switch (idef->id_text[2]) {
  408. case 'F': /* __FILE__ */
  409. FilNamBuf[0] = '"';
  410. strcpy(&FilNamBuf[1], FileName);
  411. strcat(FilNamBuf, "\"");
  412. mac->mc_text = FilNamBuf;
  413. mac->mc_length = strlen(FilNamBuf);
  414. break;
  415. case 'L': /* __LINE__ */
  416. mac->mc_text = long2str((long)LineNumber, 10);
  417. mac->mc_length = strlen(mac->mc_text);
  418. break;
  419. default:
  420. crash("(macro_func)");
  421. /*NOTREACHED*/
  422. }
  423. }
  424. macro2buffer(repl, idf, args)
  425. register struct repl *repl;
  426. register struct idf *idf;
  427. register struct args *args;
  428. {
  429. /* macro2buffer expands the replacement list and places the
  430. result onto the replacement buffer. It deals with the #
  431. and ## operators, and inserts the actual parameters.
  432. The argument buffer contains the raw argument (needed
  433. for the ## operator), and the expanded argument (for
  434. all other parameter substitutions).
  435. The grammar of the replacement list is:
  436. repl_list: TOKEN repl_list
  437. | PARAMETER repl_list
  438. | '#' PARAMETER
  439. | TOKEN '##' TOKEN
  440. | PARAMETER '##' TOKEN
  441. | TOKEN '##' PARAMETER
  442. | PARAMETER '##' PARAMETER
  443. ;
  444. As the grammar indicates, we could make a DFA and
  445. use this finite state machine for the replacement
  446. list parsing (inserting the arguments, etc.).
  447. Currently we go through the replacement list in a
  448. linear fashion. This is VERY expensive, something
  449. smarter should be done (but even a DFA is O(|s|)).
  450. */
  451. register char *ptr = idf->id_macro->mc_text;
  452. register char *tmpptr;
  453. int err = 0;
  454. char *stringify();
  455. while (*ptr) {
  456. assert(repl->r_ptr < &(repl->r_text[LAPBUF]));
  457. if (*ptr == '\'' || *ptr == '"') {
  458. register int delim = *ptr;
  459. do {
  460. *repl->r_ptr++ = *ptr;
  461. if (*ptr == '\\')
  462. *repl->r_ptr++ = *++ptr;
  463. if (*ptr == '\0') {
  464. error("unterminated string");
  465. *repl->r_ptr = '\0';
  466. return;
  467. }
  468. ptr++;
  469. } while (*ptr != delim || *ptr == '\0');
  470. *repl->r_ptr++ = *ptr++;
  471. } else if (*ptr == '#') {
  472. if (*++ptr == '#') {
  473. /* ## - paste operator */
  474. ptr++;
  475. /* trim the actual replacement list */
  476. --repl->r_ptr;
  477. while (is_wsp(*repl->r_ptr)
  478. && repl->r_ptr >= repl->r_text)
  479. --repl->r_ptr;
  480. /* ## occurred at the beginning of the
  481. replacement list.
  482. */
  483. if (repl->r_ptr == repl->r_text
  484. && is_wsp(*repl->r_text)) {
  485. err = 1;
  486. break;
  487. }
  488. while(*repl->r_ptr == TOKSEP
  489. && repl->r_ptr >= repl->r_text)
  490. --repl->r_ptr;
  491. tmpptr = repl->r_ptr;
  492. ++repl->r_ptr;
  493. /* skip space in macro replacement list */
  494. while ((*ptr & FORMALP) == 0 && is_wsp(*ptr))
  495. ptr++;
  496. /* ## occurred at the end of the replacement list.
  497. */
  498. if (*ptr & FORMALP) {
  499. register int n = *ptr++ & 0177;
  500. register char *p;
  501. assert(n > 0);
  502. p = args->a_rawvec[n-1];
  503. if (p) { /* else macro argument missing */
  504. while (is_wsp(*p))
  505. p++;
  506. if (*p == NOEXPM) p++;
  507. while (*p)
  508. *repl->r_ptr++ = *p++;
  509. }
  510. if (in_idf(*tmpptr + 1)) {
  511. while (in_idf(*tmpptr)
  512. && tmpptr >= repl->r_text)
  513. tmpptr--;
  514. if (*tmpptr == NOEXPM) *tmpptr = TOKSEP;
  515. }
  516. } else if (*ptr == '\0') {
  517. err = 1;
  518. break;
  519. } else {
  520. if (in_idf(*ptr)) {
  521. while (in_idf(*tmpptr)
  522. && tmpptr >= repl->r_text)
  523. tmpptr--;
  524. if (*tmpptr == NOEXPM) *tmpptr = TOKSEP;
  525. }
  526. }
  527. } else /* # operator */
  528. ptr = stringify(repl, ptr, args);
  529. } else if (*ptr & FORMALP) {
  530. /* insert actual parameter */
  531. register int n = *ptr++ & 0177;
  532. register char *p, *q;
  533. assert(n > 0);
  534. /* This is VERY dirty, we look ahead for the
  535. ## operater. If it's found we use the raw
  536. argument buffer instead of the expanded
  537. one.
  538. */
  539. for (p = ptr; (*p & FORMALP) == 0 && is_wsp(*p); p++)
  540. /* EMPTY */;
  541. if (*p == '#' && p[1] == '#')
  542. q = args->a_rawvec[n-1];
  543. else
  544. q = args->a_expvec[n-1];
  545. p = repl->r_ptr;
  546. if (q) /* else macro argument missing */
  547. while (*q)
  548. *repl->r_ptr++ = *q++;
  549. if (*repl->r_ptr != TOKSEP)
  550. *repl->r_ptr++ = TOKSEP;
  551. } else
  552. *repl->r_ptr++ = *ptr++;
  553. }
  554. *repl->r_ptr = '\0';
  555. if (err)
  556. error("illegal use of ## operator");
  557. }
  558. char *
  559. stringify(repl, ptr, args)
  560. register struct repl *repl;
  561. register char *ptr;
  562. register struct args *args;
  563. {
  564. /* If a parameter is immediately preceded by a # token
  565. both are replaced by a single string literal that
  566. contains the spelling of the token sequence for the
  567. corresponding argument.
  568. Each occurrence of white space between the argument's
  569. tokens become a single space character in the string
  570. literal. White spaces before the first token and after
  571. the last token comprising the argument are deleted.
  572. To retain the original spelling we insert backslashes
  573. as appropriate. We only escape backslashes if they
  574. occure within string tokens.
  575. */
  576. register int space = 1; /* skip leading spaces */
  577. register int delim = 0; /* string or character constant delim */
  578. register int backslash = 0; /* last character was a \ */
  579. /* skip spaces macro replacement list */
  580. while ((*ptr & FORMALP) == 0 && is_wsp(*ptr))
  581. ptr++;
  582. if (*ptr & FORMALP) {
  583. register int n = *ptr++ & 0177;
  584. register char *p;
  585. assert(n != 0);
  586. p = args->a_rawvec[n-1];
  587. *repl->r_ptr++ = '"';
  588. while (*p) {
  589. if (is_wsp(*p)) {
  590. if (!space) {
  591. space = 1;
  592. *repl->r_ptr++ = ' ';
  593. }
  594. p++;
  595. continue;
  596. }
  597. space = 0;
  598. if (!delim && (*p == '"' || *p == '\''))
  599. delim = *p;
  600. else if (*p == delim && !backslash)
  601. delim = 0;
  602. backslash = *p == '\\';
  603. if (*p == '"' || (delim && *p == '\\'))
  604. *repl->r_ptr++ = '\\';
  605. if (*p == TOKSEP || *p == NOEXPM) p++;
  606. else *repl->r_ptr++ = *p++;
  607. }
  608. /* trim spaces in the replacement list */
  609. for (--repl->r_ptr; is_wsp(*repl->r_ptr); repl->r_ptr--)
  610. /* EMPTY */;
  611. *++repl->r_ptr = '"';
  612. ++repl->r_ptr; /* oops, one to far */
  613. } else
  614. error("illegal use of # operator");
  615. *repl->r_ptr = '\0';
  616. return ptr;
  617. }
  618. stash(repl, ch, stashraw)
  619. struct repl *repl;
  620. register int ch;
  621. int stashraw;
  622. {
  623. /* Stash characters into the macro expansion buffer.
  624. */
  625. register struct args *args = repl->r_args;
  626. if (args->a_expptr >= &(args->a_expbuf[ARGBUF]))
  627. fatal("macro argument buffer overflow");
  628. *args->a_expptr++ = ch;
  629. if (stashraw) {
  630. if (args->a_rawptr >= &(args->a_rawbuf[ARGBUF]))
  631. fatal("raw macro argument buffer overflow");
  632. *args->a_rawptr++ = ch;
  633. }
  634. }