/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1986-2009 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* preprocessor control directive support
*/
#include "pplib.h"
#include <regex.h>
struct edit
{
};
struct map
{
};
#define RESTORE (COLLECTING|CONDITIONAL|DEFINITION|DIRECTIVE|DISABLE|EOF2NL|HEADER|NOSPACE|NOVERTICAL|PASSEOF|STRIP)
/*
* common predicate assertion operations
* op is DEFINE or UNDEF
*/
static void
{
register struct pplist* a;
register struct pplist* p;
register struct pplist* q;
{
case DEFINE:
goto mark;
case UNDEF:
a = 0;
goto unmark;
}
{
p = 0;
q = a;
while (q)
{
{
q = q->next;
if (p) p->next = q;
else a = q;
}
else
{
p = q;
q = q->next;
}
}
{
return;
}
}
{
p->next = a;
mark:
}
}
/*
* tokenize string ppop()
*
* op PP_* op
* name option name
* s string of option values
* n option sense
* flags TOKOP_* flags
*/
static void
{
register int c;
register char* t;
else if (flags & TOKOP_STRING)
{
PUSH_LINE(s);
for (;;)
{
c = pplex();
if (!c) break;
if (c != ' ')
}
POP_LINE();
}
else do
{
while (*s == ' ') s++;
for (t = s; *t && *t != ' '; t++);
if (*t) *t++ = 0;
else t = 0;
} while (s = t);
}
/*
* return symbol pointer for next token macro (re)definition
*/
static struct ppsymbol*
{
{
return 0;
}
{
return 0;
}
return sym;
}
/*
* get one space canonical pplex() line, sans '\n', and place in p
* x is max+1 pos in p
* 0 returned if line too large
* otherwise end of p ('\0') returned
*/
static char*
{
register int c;
register char* s;
char* b;
long restore;
b = p;
while ((c = pplex()) != '\n')
{
if (disable)
{
if (c == ' ')
/*ignore*/;
else if (disable == 1)
else
{
disable = 0;
if (c == ':')
}
}
while (*p = *s++)
if (++p >= x)
{
p = 0;
goto done;
}
}
if (p > b && *(p - 1) == ' ')
p--;
if (p >= x)
p = 0;
else
*p = 0;
done:
return p;
}
/*
* regex error handler
*/
void
{
regfree(p);
}
/*
* process a single directive line
*/
int
ppcontrol(void)
{
register char* p;
register int c;
register int n;
register char* s;
int o;
int directive;
long restore;
char* v;
int emitted;
union
{
char* string;
int type;
} var;
static int i0;
static int i1;
static int i2;
static int i3;
static int i4;
static long n1;
static long n2;
static long n3;
static char* p0;
static char* p1;
static char* p2;
static char* p3;
static char* p4;
static char* p5;
static char* p6;
#if MACKEYARGS
#endif
emitted = 0;
#if COMPATIBLE
if ((pp.state & (COMPATIBILITY|STRICT)) == COMPATIBILITY || (pp.mode & HOSTED)) pp.state &= ~NOVERTICAL;
#else
#endif
switch (c = pplex())
{
case T_DECIMAL:
case T_OCTAL:
goto linesync;
case T_ID:
{
case ELIF:
goto eatdirective;
{
goto eatdirective;
}
{
goto eatdirective;
}
{
goto eatdirective;
}
{
goto else_ifdef;
}
{
}
goto eatdirective;
case ELSE:
goto eatdirective;
if ((pp.option & ELSEIF) && (c = pplex()) == T_ID && ((n = (int)hashref(pp.dirtab, pp.token)) == IF || n == IFDEF || n == IFNDEF))
{
directive = n;
goto else_if;
}
else
{
{
}
else
{
}
}
goto enddirective;
case ENDIF:
goto eatdirective;
{
else
{
}
}
goto enddirective;
case IF:
case IFDEF:
case IFNDEF:
goto eatdirective;
pushcontrol();
{
goto eatdirective;
}
{
{
else
{
}
}
}
else
{
sym = 0;
}
goto enddirective;
case INCLUDE:
{
c = pplex();
goto eatdirective;
}
switch (c = pplex())
{
case T_STRING:
/*FALLTHROUGH*/
case T_HEADER:
{
break;
}
break;
case '<':
/*
* HEADEREXPAND|HEADEREXPANDALL gets us here
*/
while ((c = pplex()) && c != '>')
{
v = p + 1;
p--;
}
*p++ = 0;
c = T_HEADER;
goto header;
default:
goto eatdirective;
}
goto enddirective;
case 0:
{
/*UNDENT*/
*p++ = '#';
p0 = p;
if (!p6)
{
*p0 = 0;
c = 0;
goto eatdirective;
}
p5 = *p ? p + 1 : 0;
n = 0;
{
{
n = c;
}
}
else if (i1 != REG_NOMATCH)
c = '\n';
{
{
*p0 = 0;
}
{
n = 0;
{
n++;
break;
}
else if (i0 != REG_NOMATCH)
if (n && *p)
{
while (*s = *p++) s++;
*s++ = '\n';
*s = 0;
error_info.line++;
error_info.line--;
}
}
goto donedirective;
}
{
*p0 = 0;
}
pass:
if (!(*pp.control & SKIP) && pp.pragma && !(pp.state & NOTEXT) && (directive == PRAGMA || !(pp.mode & INIT)))
{
*p0 = 0;
if (p4)
{
{
{
s = p;
while (*p)
*s = 0;
}
}
*p4 = 0;
}
{
s = p;
while (p < p6) switch (*s++ = *p++)
{
case 0:
s = p;
break;
case MARK:
p++;
break;
}
*s = 0;
}
emitted = 1;
}
goto donedirective;
/*INDENT*/
}
}
switch (directive)
{
#if MACDEF
case ENDMAC:
c = pplex();
goto enddirective;
#endif
#if MACDEF
case MACDEF:
/*FALLTHROUGH*/
#endif
case DEFINE:
goto assertion;
if (c == '<')
{
n = 1;
c = pplex();
}
else
n = 0;
goto eatdirective;
goto eatdirective;
if (n)
goto tuple;
sym->flags &= ~(SYM_BUILTIN|SYM_EMPTY|SYM_FINAL|SYM_FUNCTION|SYM_INIT|SYM_INITIAL|SYM_MULTILINE|SYM_NOEXPAND|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
#if MACDEF
#endif
switch (c = pplex())
{
case '(':
#if MACKEYARGS
{
n = 2 * MAXTOKEN;
{
{
*p++ = ' ';
*p = 0;
}
switch (c = pplex())
{
case '=':
c = pplex();
break;
case ',':
break;
default:
goto endformals;
}
p0 = 0;
for (;;)
{
switch (c)
{
case '\n':
goto endformals;
case '(':
p0++;
break;
case ')':
if (!p0--)
{
goto endformals;
}
break;
case ',':
if (!p0)
{
goto nextformal;
}
break;
case ' ':
break;
}
if (p > &mac->formals[n - MAXTOKEN] && (s = newof(mac->formals, char, n += MAXTOKEN, 0)) != mac->formals)
{
{
}
}
c = pplex();
}
{
c = ',';
break;
}
}
endformals: /*NOP*/;
}
else
#endif
{
c = pplex();
#if COMPATIBLE
{
while ((c = pplex()) == ',');
}
#endif
for (;;)
{
if (c == T_VARIADIC)
{
v = __va_args__;
}
else if (c == T_ID)
{
else if (streq(v, __va_args__))
}
else
break;
{
STRAPP(p, v, s);
}
else
if ((c = pplex()) == ',')
{
c = pplex();
#if COMPATIBLE
{
while ((c = pplex()) == ',');
}
#endif
}
else if (c != T_VARIADIC)
break;
else
{
c = pplex();
break;
}
}
{
}
}
{
}
switch (c)
{
case ')':
#if MACKEYARGS
#else
#endif
c = pplex();
break;
default:
{
}
goto eatdirective;
}
break;
case ' ':
case '\t':
c = pplex();
break;
}
n = 2 * MAXTOKEN;
#if MACKEYARGS
p1 = p;
#endif
n1 = 0;
#if MACDEF
#endif
switch (c)
{
case '+':
case '-':
case '&':
case '|':
case '<':
case '>':
case ':':
case '=':
*p++ = ' ';
break;
}
o = 0;
for (;;)
{
switch (c)
{
case T_ID:
{
#if COMPATIBLE
#endif
if (var.type != TOK_TOKCAT && p > mac->value && *(p - 1) != ' ' && !(pp.option & PRESERVE)) *p++ = ' ';
*p++ = MARK;
#if COMPATIBLE
else
#endif
*p++ = c + ARGOFFSET;
if ((pp.state & WARN) && !(pp.mode & (HOSTED|RELAX)) && var.type != TOK_TOKCAT && !(var.type & TOK_ID))
{
while ((c = *s++) && (c == ' ' || c == '\t'));
if (c == '\n')
c = 0;
else if (c == '*' && *s == ')')
c = ')';
c = 0;
if (o != '.' && o != T_PTRMEM)
{
o = 0;
if (!((o == 0 || o == '(' || o == ')' || o == '[' || o == ']' || o == ',' || o == '|' || o == ';' || o == '{' || o == '}') && (c == '(' || c == ')' || c == '[' || c == ']' || c == ',' || c == '|' || c == ';' || c == '}' || c == 0)) && !(o == '*' && c == ')'))
error(1, "%s: %s: formal should be parenthesized in macro value (t=%x o=%#c c=%#c)", sym->name, pp.token, var.type, o, c);
}
}
c = '>';
goto checkvalue;
}
{
case V_DEFAULT:
case V_EMPTY:
break;
}
{
goto checkvalue;
}
break;
case '#':
#if MACDEF
#else
#endif
c = pplex();
if (c == '@')
{
c = pplex();
i4 = 'S';
}
else i4 = 'Q';
break;
{
#if MACDEF
{
{
{
case ENDMAC:
if (!i2--) goto gotdefinition;
break;
case INCLUDE:
/* PARSE HEADER constant */
break;
case MACDEF:
i2++;
break;
}
*p++ = '#';
}
}
else
#endif
#if COMPATIBLE
else
#endif
}
else
{
*p++ = MARK;
*p++ = i4;
*p++ = c + ARGOFFSET;
goto checkvalue;
}
break;
case T_TOKCAT:
else
{
if (*(p - 1) == ' ') p--;
}
c = pplex();
continue;
case '(':
{
n1++;
}
else
{
}
break;
case ')':
break;
case T_STRING:
case T_CHARCONST:
#if COMPATIBLE
/*UNDENT*/
{
char* v;
for (;;)
{
if (!*s) goto checkvalue;
if (ppisid(*s))
{
v = s;
while (ppisid(*++s));
i1 = *s;
*s = 0;
{
*p++ = MARK;
*p++ = 'C';
*p++ = c + ARGOFFSET;
{
case '"':
break;
case '\'':
break;
}
goto quotearg;
}
STRCOPY2(p, v);
*s = i1;
}
else *p++ = *s++;
}
}
/*INDENT*/
#endif
break;
case '\n':
#if MACDEF
{
{
error_info.line++;
if (!i3++)
goto checkvalue;
break;
}
}
#endif
goto gotdefinition;
case 0:
c = '\n';
goto gotdefinition;
#if COMPATIBLE
case ' ':
goto checkvalue;
case '\t':
{
while ((c = pplex()) == '\t');
if (c == T_ID)
{
}
continue;
}
goto checkvalue;
#endif
case MARK:
/*FALLTHROUGH*/
default:
break;
}
o = c;
{
}
#if MACDEF
#endif
c = pplex();
}
if (p > mac->value && (pp.option & PLUSPLUS) && (pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY)
switch (o)
{
case '+':
case '-':
case '&':
case '|':
case '<':
case '>':
case ':':
case '=':
*p++ = ' ';
break;
}
*p = 0;
#if MACKEYARGS
{
{
}
}
else
#endif
{
if ((i0 & SYM_FUNCTION) != (sym->flags & SYM_FUNCTION) || old.arity != mac->arity || !streq(old.value, mac->value)) goto redefined;
{
}
{
#if MACKEYARGS
{
if (!streq(mac->formkeys[n].name, old.formkeys[n].name) || !streq(mac->formkeys[n].value, old.formkeys[n].value))
goto redefined;
}
else
#endif
}
#if MACKEYARGS
{
}
else
#endif
{
}
goto benign;
#if MACKEYARGS
#endif
#if MACKEYARGS
#endif
}
if ((pp.option & (DEFINITIONS|PREDEFINITIONS|REGUARD)) && !sym->hidden && !(sym->flags & SYM_MULTILINE) && ((pp.option & PREDEFINITIONS) || !(pp.mode & INIT)) && ((pp.option & (DEFINITIONS|PREDEFINITIONS)) || !(pp.state & NOTEXT)))
{
ppsync();
{
ppputchar('(');
ppputchar(')');
}
{
ppputchar(' ');
i0 = 0;
while (n = *p++)
{
{
ppputchar(n);
}
else
{
if (n == 'Q')
ppputchar('#');
else if (i0)
{
ppputchar('#');
ppputchar('#');
}
while ((n = *s++) && n != ',')
ppputchar(n);
{
ppputchar('#');
ppputchar('#');
}
i0 = 0;
}
ppcheckout();
}
}
emitted = 1;
}
if (pp.macref) (*pp.macref)(sym, error_info.file, n2, mac ? error_info.line - n2 + 1 : REF_UNDEF, mac ? strsum(mac->value, (long)mac->arity) : 0L);
break;
c = pplex();
if (c != T_ID)
{
goto eatdirective;
}
{
case X_DEFINED:
case X_EXISTS:
case X_STRCMP:
goto eatdirective;
case X_SIZEOF:
goto eatdirective;
}
switch (pppredargs())
{
case T_ID:
case T_STRING:
break;
case 0:
break;
default:
goto eatdirective;
}
break;
rp = 0;
{
break;
if (!tp)
{
if (rp)
{
}
else
{
}
}
}
if (!rp || c != '>')
else
{
n = 2 * MAXTOKEN;
p = v = oldof(0, char, 0, n);
while ((c = pplex()) && c != '\n')
if (p > v || c != ' ')
{
{
c = p - v;
v = s;
p = v + c;
}
}
while (p > v && *(p - 1) == ' ')
p--;
n = p - v;
}
goto benign;
case WARNING:
/*FALLTHROUGH*/
case ERROR:
while ((c = pplex()) != '\n')
{
}
*p = 0;
error(n, "%s", p);
break;
case LET:
if ((c = pplex()) != '=')
{
goto eatdirective;
}
{
#if MACKEYARGS
else
#endif
}
else
{
n = 0;
}
if (n < ++c)
{
}
goto benign;
case LINE:
if ((c = pplex()) == '#')
{
c = pplex();
}
{
goto eatdirective;
}
n = error_info.line;
switch (c = pplex())
{
case T_STRING:
s = error_info.file;
pathcanon(p, 0);
switch (c = pplex())
{
case '\n':
break;
case T_DECIMAL:
case T_OCTAL:
break;
default:
break;
}
{
if (error_info.file != s)
{
{
case PP_sync_push:
break;
case PP_sync_pop:
break;
case PP_sync_ignore:
else
{
error_info.file = s;
}
break;
default:
if (*s)
{
else
{
}
}
break;
}
}
}
break;
case '\n':
break;
default:
break;
}
else
{
{
#if CATSTRINGS
else
#endif
{
{
}
}
}
}
break;
case PRAGMA:
/*
* #pragma [STDC] [pass:] [no]option [arg ...]
*
* pragma args are not expanded by default
*
* if STDC is present then it is silently passed on
*
* if pass is pp.pass then the option is used
* and verified but is not passed on
*
* if pass is omitted then the option is passed on
*
* otherwise if pass is non-null and not pp.pass then
* the option is passed on but not used
*
* if the line does not match this form then
* it is passed on unchanged
*
* #directive pass: option [...]
* ^ ^ ^ ^ ^ ^ ^ ^
* pp.valbuf p0 p1 p2 p3 p4 p5 p6
*
* p? 0 if component omitted
* i0 0 if ``no''option
*/
*p++ = '#';
p0 = p;
{
*p0 = 0;
c = 0;
goto eatdirective;
}
p1 = ++p;
while (ppisid(*p))
p++;
if (p == p1)
{
p5 = p;
p4 = 0;
p3 = 0;
p2 = 0;
p1 = 0;
}
else if (*p != ':')
{
p5 = *p ? p + (*p == ' ') : 0;
p4 = p;
p2 = 0;
p1 = 0;
}
else
{
p2 = p++;
p3 = p;
while (ppisid(*p))
p++;
if (p == p3)
{
p3 = 0;
p2 = 0;
p1 = 0;
}
else
p4 = p;
}
goto pass;
if (!p3)
goto checkmap;
if (p1)
{
*p2 = 0;
*p2 = ':';
if (!n)
goto checkmap;
}
else
n = 0;
*p4 = 0;
if (((i1 = (int)hashref(pp.strtab, p3 + (i0 ? 0 : 2))) < 1 || i1 > X_last_option) && (i0 || (i1 = (int)hashref(pp.strtab, p3)) > X_last_option))
i1 = 0;
{
goto donedirective;
{
n = 0;
}
i1 = 0;
}
if (!n)
{
{
goto checkmap;
}
n = 1;
}
else if (!i1)
p = p5;
switch (i1)
{
case X_ALLMULTIPLE:
break;
case X_ALLPOSSIBLE:
break;
case X_BUILTIN:
break;
case X_CATLITERAL:
setoption(STRINGSPLIT, 0);
break;
case X_CDIR:
break;
case X_CHECKPOINT:
#if CHECKPOINT
ppload(p);
#else
#endif
break;
case X_CHOP:
break;
case X_COMPATIBILITY:
break;
case X_DEBUG:
break;
case X_ELSEIF:
break;
case X_EXTERNALIZE:
break;
case X_FINAL:
break;
case X_HEADEREXPAND:
break;
case X_HEADEREXPANDALL:
break;
case X_HIDE:
case X_NOTE:
PUSH_LINE(p);
/* UNDENT...*/
while (c = pplex())
{
{
{
}
else if (i0)
{
{
{
sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
}
}
}
{
{
sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
}
{
{
}
}
}
}
}
/*...INDENT*/
POP_LINE();
break;
case X_HOSTDIR:
break;
case X_HOSTED:
break;
case X_HOSTEDTRANSITION:
break;
case X_ID:
break;
case X_IGNORE:
break;
case X_INCLUDE:
break;
case X_INITIAL:
break;
case X_KEYARGS:
break;
case X_LINE:
break;
case X_LINEBASE:
break;
case X_LINEFILE:
break;
case X_LINEID:
break;
case X_LINETYPE:
break;
case X_MACREF:
if (!p)
{
{
}
}
else if (s = strchr(p, ' '))
{
{
*s++ = 0;
(*pp.macref)(pprefmac(p, REF_CREATE), error_info.file, error_info.line - (c == REF_NORMAL ? 2 : 1), c, (s = strchr(s, ' ')) ? strtol(s, NiL, 0) : 0L);
}
}
break;
case X_MAP:
/*UNDENT*/
/*
*/
if (!i0)
{
goto donedirective;
}
if (!p5)
{
goto donedirective;
}
{
{
}
{
}
}
{
if (c)
goto eatmap;
}
/*
* /from/
*/
{
goto eatmap;
}
/*
*/
edit = 0;
{
{
goto eatmap;
}
if (edit)
else
if (!(i0 = regcomp(&edit->re, s, REG_AUGMENTED|REG_DELIMITED|REG_LENIENT|REG_NULL)) && !(i0 = regsubcomp(&edit->re, s += edit->re.re_npat, NiL, 0, 0)))
if (i0)
if (*s)
{
goto eatmap;
}
}
if (c)
{
goto eatmap;
}
POP_LINE();
/*INDENT*/
break;
case X_MAPINCLUDE:
break;
case X_MODERN:
break;
case X_MULTIPLE:
n = 1;
break;
case X_NATIVE:
break;
case X_OPSPACE:
break;
case X_PASSTHROUGH:
break;
case X_PEDANTIC:
break;
case X_PLUSCOMMENT:
break;
case X_PLUSPLUS:
break;
case X_PLUSSPLICE:
break;
case X_PRAGMAEXPAND:
break;
case X_PRAGMAFLAGS:
break;
case X_PREDEFINED:
break;
case X_PREFIX:
break;
case X_PRESERVE:
{
setmode(CATLITERAL, 0);
ppop(PP_TRANSITION, 0);
setoption(STRINGSPLIT, 0);
}
break;
case X_PROTOTYPED:
/*
* this option doesn't bump the token count
*/
n = 1;
#if PROTOTYPE
#else
#endif
break;
case X_PROTO:
break;
case X_QUOTE:
break;
case X_READONLY:
break;
case X_REGUARD:
break;
case X_RESERVED:
break;
case X_SPACEOUT:
break;
case X_SPLICECAT:
break;
case X_SPLICESPACE:
break;
case X_STANDARD:
break;
case X_STRICT:
break;
case X_STRINGSPAN:
break;
case X_STRINGSPLIT:
setmode(CATLITERAL, 0);
break;
case X_SYSTEM_HEADER:
if (i0)
{
}
else
{
}
break;
case X_TEST:
break;
case X_TEXT:
break;
case X_TRANSITION:
break;
case X_TRUNCATE:
break;
case X_VENDOR:
break;
case X_VERSION:
{
emitted = 1;
}
break;
case X_WARN:
break;
case X_ZEOF:
break;
#if DEBUG
case 0:
case X_INCLUDED:
case X_NOTICED:
case X_OPTION:
case X_STATEMENT:
break;
default:
break;
#endif
}
if (!n)
goto checkmap;
goto donedirective;
case RENAME:
{
goto eatdirective;
}
goto eatdirective;
{
goto eatdirective;
}
{
goto eatdirective;
}
{
{
error(2, "%s: macro is %s", var.symbol->name, (var.symbol->flags & SYM_READONLY) ? "readonly" : "active");
goto eatdirective;
}
}
sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
break;
case UNDEF:
{
goto eatdirective;
}
{
{
{
goto eatdirective;
}
}
if ((pp.option & (DEFINITIONS|PREDEFINITIONS|REGUARD)) && !sym->hidden && !(sym->flags & SYM_MULTILINE) && ((pp.option & PREDEFINITIONS) || !(pp.mode & INIT)) && ((pp.option & (DEFINITIONS|PREDEFINITIONS)) || !(pp.state & NOTEXT)))
{
ppsync();
emitted = 1;
}
sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
goto benign;
}
break;
#if DEBUG
default:
goto eatdirective;
#endif
}
break;
case '\n':
break;
default:
goto eatdirective;
}
#if COMPATIBLE
#else
if (c != '\n')
#endif
{
}
if (c != '\n')
{
while (pplex() != '\n');
}
#if _HUH_2002_05_09
#endif
{
switch (directive)
{
case LINE:
return 0;
case INCLUDE:
if (pp.include)
{
error_info.line++;
pp.include = 0;
return 0;
}
{
}
/*FALLTHROUGH*/
default:
/*FALLTHROUGH*/
case ENDIF:
error_info.line++;
if (emitted)
{
ppputchar('\n');
ppcheckout();
}
else
{
}
return 0;
}
}
error_info.line++;
return 0;
}
/*
* grow the pp nesting control stack
*/
void
ppnest(void)
{
int oz;
int nz;
long adjust;
long* op;
long* np;
{
do
{
}
}