1 /***** spin: sym.c *****/
2 
3 /*
4  * This file is part of the public release of Spin. It is subject to the
5  * terms in the LICENSE file that is included in this source directory.
6  * Tool documentation is available at http://spinroot.com
7  */
8 
9 #include "spin.h"
10 #include "y.tab.h"
11 
12 extern Symbol	*Fname, *owner;
13 extern int	lineno, depth, verbose, NamesNotAdded, deadvar;
14 extern int	has_hidden, m_loss, old_scope_rules;
15 extern short	has_xu;
16 extern char	CurScope[MAXSCOPESZ];
17 
18 Symbol	*context = ZS;
19 Ordered	*all_names = (Ordered *)0;
20 int	Nid_nr = 0;
21 
22 Mtypes_t	*Mtypes;
23 Lextok		*runstmnts = ZN;
24 
25 static Ordered	*last_name = (Ordered *)0;
26 static Symbol	*symtab[Nhash+1];
27 
28 static int
samename(Symbol * a,Symbol * b)29 samename(Symbol *a, Symbol *b)
30 {
31 	if (!a && !b) return 1;
32 	if (!a || !b) return 0;
33 	return !strcmp(a->name, b->name);
34 }
35 
36 unsigned int
hash(const char * s)37 hash(const char *s)
38 {	unsigned int h = 0;
39 
40 	while (*s)
41 	{	h += (unsigned int) *s++;
42 		h <<= 1;
43 		if (h&(Nhash+1))
44 			h |= 1;
45 	}
46 	return h&Nhash;
47 }
48 
49 void
disambiguate(void)50 disambiguate(void)
51 {	Ordered *walk;
52 	Symbol *sp;
53 	char *n, *m;
54 
55 	if (old_scope_rules)
56 		return;
57 
58 	/* prepend the scope_prefix to the names */
59 
60 	for (walk = all_names; walk; walk = walk->next)
61 	{	sp = walk->entry;
62 		if (sp->type != 0
63 		&&  sp->type != LABEL
64 		&&  strlen((const char *)sp->bscp) > 1)
65 		{	if (sp->context)
66 			{	m = (char *) emalloc(strlen((const char *)sp->bscp) + 1);
67 				sprintf(m, "_%d_", sp->context->sc);
68 				if (strcmp((const char *) m, (const char *) sp->bscp) == 0)
69 				{	continue;
70 				/* 6.2.0: only prepend scope for inner-blocks,
71 				   not for top-level locals within a proctype
72 				   this means that you can no longer use the same name
73 				   for a global and a (top-level) local variable
74 				 */
75 			}	}
76 
77 			n = (char *) emalloc(strlen((const char *)sp->name)
78 				+ strlen((const char *)sp->bscp) + 1);
79 			sprintf(n, "%s%s", sp->bscp, sp->name);
80 			sp->name = n;	/* discard the old memory */
81 	}	}
82 }
83 
84 Symbol *
lookup(char * s)85 lookup(char *s)
86 {	Symbol *sp; Ordered *no;
87 	unsigned int h = hash(s);
88 
89 	if (old_scope_rules)
90 	{	/* same scope - global refering to global or local to local */
91 		for (sp = symtab[h]; sp; sp = sp->next)
92 		{	if (strcmp(sp->name, s) == 0
93 			&&  samename(sp->context, context)
94 			&&  samename(sp->owner, owner))
95 			{	return sp;		/* found */
96 		}	}
97 	} else
98 	{	/* added 6.0.0: more traditional, scope rule */
99 		for (sp = symtab[h]; sp; sp = sp->next)
100 		{	if (strcmp(sp->name, s) == 0
101 			&&  samename(sp->context, context)
102 			&&  (strcmp((const char *)sp->bscp, CurScope) == 0
103 			||   strncmp((const char *)sp->bscp, CurScope, strlen((const char *)sp->bscp)) == 0)
104 			&&  samename(sp->owner, owner))
105 			{
106 				if (!samename(sp->owner, owner))
107 				{	printf("spin: different container %s\n", sp->name);
108 					printf("	old: %s\n", sp->owner?sp->owner->name:"--");
109 					printf("	new: %s\n", owner?owner->name:"--");
110 				/*	alldone(1);	*/
111 				}
112 				return sp;		/* found */
113 	}	}	}
114 
115 	if (context)				/* in proctype, refers to global */
116 	for (sp = symtab[h]; sp; sp = sp->next)
117 	{	if (strcmp(sp->name, s) == 0
118 		&& !sp->context
119 		&&  samename(sp->owner, owner))
120 		{	return sp;		/* global */
121 	}	}
122 
123 	sp = (Symbol *) emalloc(sizeof(Symbol));
124 	sp->name = (char *) emalloc(strlen(s) + 1);
125 	strcpy(sp->name, s);
126 	sp->nel = 1;
127 	sp->setat = depth;
128 	sp->context = context;
129 	sp->owner = owner;			/* if fld in struct */
130 	sp->bscp = (unsigned char *) emalloc(strlen((const char *)CurScope)+1);
131 	strcpy((char *)sp->bscp, CurScope);
132 
133 	if (NamesNotAdded == 0)
134 	{	sp->next = symtab[h];
135 		symtab[h] = sp;
136 		no = (Ordered *) emalloc(sizeof(Ordered));
137 		no->entry = sp;
138 		if (!last_name)
139 			last_name = all_names = no;
140 		else
141 		{	last_name->next = no;
142 			last_name = no;
143 	}	}
144 
145 	return sp;
146 }
147 
148 void
trackvar(Lextok * n,Lextok * m)149 trackvar(Lextok *n, Lextok *m)
150 {	Symbol *sp = n->sym;
151 
152 	if (!sp) return;	/* a structure list */
153 	switch (m->ntyp) {
154 	case NAME:
155 		if (m->sym->type != BIT)
156 		{	sp->hidden |= 4;
157 			if (m->sym->type != BYTE)
158 				sp->hidden |= 8;
159 		}
160 		break;
161 	case CONST:
162 		if (m->val != 0 && m->val != 1)
163 			sp->hidden |= 4;
164 		if (m->val < 0 || m->val > 256)
165 			sp->hidden |= 8; /* ditto byte-equiv */
166 		break;
167 	default:	/* unknown */
168 		sp->hidden |= (4|8); /* not known bit-equiv */
169 	}
170 }
171 
172 void
trackrun(Lextok * n)173 trackrun(Lextok *n)
174 {
175 	runstmnts = nn(ZN, 0, n, runstmnts);
176 }
177 
178 void
checkrun(Symbol * parnm,int posno)179 checkrun(Symbol *parnm, int posno)
180 {	Lextok *n, *now, *v; int i, m;
181 	int res = 0; char buf[16], buf2[16];
182 
183 	for (n = runstmnts; n; n = n->rgt)
184 	{	now = n->lft;
185 		if (now->sym != parnm->context)
186 			continue;
187 		for (v = now->lft, i = 0; v; v = v->rgt, i++)
188 			if (i == posno)
189 			{	m = v->lft->ntyp;
190 				if (m == CONST)
191 				{	m = v->lft->val;
192 					if (m != 0 && m != 1)
193 						res |= 4;
194 					if (m < 0 || m > 256)
195 						res |= 8;
196 				} else if (m == NAME)
197 				{	m = v->lft->sym->type;
198 					if (m != BIT)
199 					{	res |= 4;
200 						if (m != BYTE)
201 							res |= 8;
202 					}
203 				} else
204 					res |= (4|8); /* unknown */
205 				break;
206 	}		}
207 	if (!(res&4) || !(res&8))
208 	{	if (!(verbose&32)) return;
209 		strcpy(buf2, (!(res&4))?"bit":"byte");
210 		sputtype(buf, parnm->type);
211 		i = (int) strlen(buf);
212 		while (i > 0 && buf[--i] == ' ') buf[i] = '\0';
213 		if (i == 0 || strcmp(buf, buf2) == 0) return;
214 		prehint(parnm);
215 		printf("proctype %s, '%s %s' could be declared",
216 			parnm->context?parnm->context->name:"", buf, parnm->name);
217 		printf(" '%s %s'\n", buf2, parnm->name);
218 	}
219 }
220 
221 void
trackchanuse(Lextok * m,Lextok * w,int t)222 trackchanuse(Lextok *m, Lextok *w, int t)
223 {	Lextok *n = m; int cnt = 1;
224 	while (n)
225 	{	if (n->lft
226 		&&  n->lft->sym
227 		&&  n->lft->sym->type == CHAN)
228 			setaccess(n->lft->sym, w?w->sym:ZS, cnt, t);
229 		n = n->rgt; cnt++;
230 	}
231 }
232 
233 void
setptype(Lextok * mtype_name,Lextok * n,int t,Lextok * vis)234 setptype(Lextok *mtype_name, Lextok *n, int t, Lextok *vis)	/* predefined types */
235 {	int oln = lineno, cnt = 1; extern int Expand_Ok;
236 
237 	while (n)
238 	{	if (n->sym->type && !(n->sym->hidden&32))
239 		{	lineno = n->ln; Fname = n->fn;
240 			fatal("redeclaration of '%s'", n->sym->name);
241 			lineno = oln;
242 		}
243 		n->sym->type = (short) t;
244 
245 		if (mtype_name && t != MTYPE)
246 		{	lineno = n->ln; Fname = n->fn;
247 			fatal("missing semi-colon after '%s'?",
248 				mtype_name->sym->name);
249 			lineno = oln;
250 		}
251 
252 		if (mtype_name && n->sym->mtype_name
253 		&& strcmp(mtype_name->sym->name, n->sym->mtype_name->name) != 0)
254 		{	fprintf(stderr, "spin: %s:%d, Error: '%s' is type '%s' but assigned type '%s'\n",
255 				n->fn->name, n->ln,
256 				n->sym->name,
257 				mtype_name->sym->name,
258 				n->sym->mtype_name->name);
259 			non_fatal("type error", (char *) 0);
260 		}
261 
262 		n->sym->mtype_name = mtype_name?mtype_name->sym:0; /* if mtype, else 0 */
263 
264 		if (Expand_Ok)
265 		{	n->sym->hidden |= (4|8|16); /* formal par */
266 			if (t == CHAN)
267 			setaccess(n->sym, ZS, cnt, 'F');
268 		}
269 
270 		if (t == UNSIGNED)
271 		{	if (n->sym->nbits < 0 || n->sym->nbits >= 32)
272 			fatal("(%s) has invalid width-field", n->sym->name);
273 			if (n->sym->nbits == 0)
274 			{	n->sym->nbits = 16;
275 				non_fatal("unsigned without width-field", 0);
276 			}
277 		} else if (n->sym->nbits > 0)
278 		{	non_fatal("(%s) only an unsigned can have width-field",
279 				n->sym->name);
280 		}
281 
282 		if (vis)
283 		{	if (strncmp(vis->sym->name, ":hide:", (size_t) 6) == 0)
284 			{	n->sym->hidden |= 1;
285 				has_hidden++;
286 				if (t == BIT)
287 				fatal("bit variable (%s) cannot be hidden",
288 					n->sym->name);
289 			} else if (strncmp(vis->sym->name, ":show:", (size_t) 6) == 0)
290 			{	n->sym->hidden |= 2;
291 			} else if (strncmp(vis->sym->name, ":local:", (size_t) 7) == 0)
292 			{	n->sym->hidden |= 64;
293 			}
294 		}
295 
296 		if (t == CHAN)
297 		{	n->sym->Nid = ++Nid_nr;
298 		} else
299 		{	n->sym->Nid = 0;
300 			if (n->sym->ini
301 			&&  n->sym->ini->ntyp == CHAN)
302 			{	Fname = n->fn;
303 				lineno = n->ln;
304 				fatal("chan initializer for non-channel %s",
305 				n->sym->name);
306 		}	}
307 
308 		if (n->sym->nel <= 0)
309 		{	lineno = n->ln; Fname = n->fn;
310 			non_fatal("bad array size for '%s'", n->sym->name);
311 			lineno = oln;
312 		}
313 
314 		n = n->rgt; cnt++;
315 	}
316 }
317 
318 static void
setonexu(Symbol * sp,int t)319 setonexu(Symbol *sp, int t)
320 {
321 	sp->xu |= t;
322 	if (t == XR || t == XS)
323 	{	if (sp->xup[t-1]
324 		&&  strcmp(sp->xup[t-1]->name, context->name))
325 		{	printf("error: x[rs] claims from %s and %s\n",
326 				sp->xup[t-1]->name, context->name);
327 			non_fatal("conflicting claims on chan '%s'",
328 				sp->name);
329 		}
330 		sp->xup[t-1] = context;
331 	}
332 }
333 
334 static void
setallxu(Lextok * n,int t)335 setallxu(Lextok *n, int t)
336 {	Lextok *fp, *tl;
337 
338 	for (fp = n; fp; fp = fp->rgt)
339 	for (tl = fp->lft; tl; tl = tl->rgt)
340 	{	if (tl->sym->type == STRUCT)
341 			setallxu(tl->sym->Slst, t);
342 		else if (tl->sym->type == CHAN)
343 			setonexu(tl->sym, t);
344 	}
345 }
346 
347 Lextok *Xu_List = (Lextok *) 0;
348 
349 void
setxus(Lextok * p,int t)350 setxus(Lextok *p, int t)
351 {	Lextok *m, *n;
352 
353 	has_xu = 1;
354 
355 	if (m_loss && t == XS)
356 	{	printf("spin: %s:%d, warning, xs tag not compatible with -m (message loss)\n",
357 			(p->fn != NULL) ? p->fn->name : "stdin", p->ln);
358 	}
359 
360 	if (!context)
361 	{	lineno = p->ln;
362 		Fname = p->fn;
363 		fatal("non-local x[rs] assertion", (char *)0);
364 	}
365 	for (m = p; m; m = m->rgt)
366 	{	Lextok *Xu_new = (Lextok *) emalloc(sizeof(Lextok));
367 		Xu_new->uiid = p->uiid;
368 		Xu_new->val = t;
369 		Xu_new->lft = m->lft;
370 		Xu_new->sym = context;
371 		Xu_new->rgt = Xu_List;
372 		Xu_List = Xu_new;
373 
374 		n = m->lft;
375 		if (n->sym->type == STRUCT)
376 			setallxu(n->sym->Slst, t);
377 		else if (n->sym->type == CHAN)
378 			setonexu(n->sym, t);
379 		else
380 		{	int oln = lineno;
381 			lineno = n->ln; Fname = n->fn;
382 			non_fatal("xr or xs of non-chan '%s'",
383 				n->sym->name);
384 			lineno = oln;
385 		}
386 	}
387 }
388 
389 Lextok **
find_mtype_list(const char * s)390 find_mtype_list(const char *s)
391 {	Mtypes_t *lst;
392 
393 	for (lst = Mtypes; lst; lst = lst->nxt)
394 	{	if (strcmp(lst->nm, s) == 0)
395 		{	return &(lst->mt);
396 	}	}
397 
398 	/* not found, create it */
399 	lst = (Mtypes_t *) emalloc(sizeof(Mtypes_t));
400 	lst->nm = (char *) emalloc(strlen(s)+1);
401 	strcpy(lst->nm, s);
402 	lst->nxt = Mtypes;
403 	Mtypes = lst;
404 	return &(lst->mt);
405 }
406 
407 void
setmtype(Lextok * mtype_name,Lextok * m)408 setmtype(Lextok *mtype_name, Lextok *m)
409 {	Lextok **mtl;	/* mtype list */
410 	Lextok *n, *Mtype;
411 	int cnt, oln = lineno;
412 	char *s = "_unnamed_";
413 
414 	if (m) { lineno = m->ln; Fname = m->fn; }
415 
416 	if (mtype_name && mtype_name->sym)
417 	{	s = mtype_name->sym->name;
418 	}
419 
420 	mtl = find_mtype_list(s);
421 	Mtype = *mtl;
422 
423 	if (!Mtype)
424 	{	*mtl = Mtype = m;
425 	} else
426 	{	for (n = Mtype; n->rgt; n = n->rgt)
427 		{	;
428 		}
429 		n->rgt = m;	/* concatenate */
430 	}
431 
432 	for (n = Mtype, cnt = 1; n; n = n->rgt, cnt++)	/* syntax check */
433 	{	if (!n->lft || !n->lft->sym
434 		||   n->lft->ntyp != NAME
435 		||   n->lft->lft)	/* indexed variable */
436 			fatal("bad mtype definition", (char *)0);
437 
438 		/* label the name */
439 		if (n->lft->sym->type != MTYPE)
440 		{	n->lft->sym->hidden |= 128;	/* is used */
441 			n->lft->sym->type = MTYPE;
442 			n->lft->sym->ini = nn(ZN,CONST,ZN,ZN);
443 			n->lft->sym->ini->val = cnt;
444 		} else if (n->lft->sym->ini->val != cnt)
445 		{	non_fatal("name %s appears twice in mtype declaration",
446 				n->lft->sym->name);
447 	}	}
448 
449 	lineno = oln;
450 	if (cnt > 256)
451 	{	fatal("too many mtype elements (>255)", (char *) 0);
452 	}
453 }
454 
455 char *
which_mtype(const char * str)456 which_mtype(const char *str) /* which mtype is str, 0 if not an mtype at all  */
457 {	Mtypes_t *lst;
458 	Lextok *n;
459 
460 	for (lst = Mtypes; lst; lst = lst->nxt)
461 	for (n = lst->mt; n; n = n->rgt)
462 	{	if (strcmp(str, n->lft->sym->name) == 0)
463 		{	return lst->nm;
464 	}	}
465 
466 	return (char *) 0;
467 }
468 
469 int
ismtype(char * str)470 ismtype(char *str)	/* name to number */
471 {	Mtypes_t *lst;
472 	Lextok *n;
473 	int cnt;
474 
475 	for (lst = Mtypes; lst; lst = lst->nxt)
476 	{	cnt = 1;
477 		for (n = lst->mt; n; n = n->rgt)
478 		{	if (strcmp(str, n->lft->sym->name) == 0)
479 			{	return cnt;
480 			}
481 			cnt++;
482 	}	}
483 
484 	return 0;
485 }
486 
487 int
sputtype(char * foo,int m)488 sputtype(char *foo, int m)
489 {
490 	switch (m) {
491 	case UNSIGNED:	strcpy(foo, "unsigned "); break;
492 	case BIT:	strcpy(foo, "bit   "); break;
493 	case BYTE:	strcpy(foo, "byte  "); break;
494 	case CHAN:	strcpy(foo, "chan  "); break;
495 	case SHORT:	strcpy(foo, "short "); break;
496 	case INT:	strcpy(foo, "int   "); break;
497 	case MTYPE:	strcpy(foo, "mtype "); break;
498 	case STRUCT:	strcpy(foo, "struct"); break;
499 	case PROCTYPE:	strcpy(foo, "proctype"); break;
500 	case LABEL:	strcpy(foo, "label "); return 0;
501 	default:	strcpy(foo, "value "); return 0;
502 	}
503 	return 1;
504 }
505 
506 
507 static int
puttype(int m)508 puttype(int m)
509 {	char buf[128];
510 
511 	if (sputtype(buf, m))
512 	{	printf("%s", buf);
513 		return 1;
514 	}
515 	return 0;
516 }
517 
518 void
symvar(Symbol * sp)519 symvar(Symbol *sp)
520 {	Lextok *m;
521 
522 	if (!puttype(sp->type))
523 		return;
524 
525 	printf("\t");
526 	if (sp->owner) printf("%s.", sp->owner->name);
527 	printf("%s", sp->name);
528 	if (sp->nel > 1 || sp->isarray == 1) printf("[%d]", sp->nel);
529 
530 	if (sp->type == CHAN)
531 		printf("\t%d", (sp->ini)?sp->ini->val:0);
532 	else if (sp->type == STRUCT && sp->Snm != NULL) /* Frank Weil, 2.9.8 */
533 		printf("\t%s", sp->Snm->name);
534 	else
535 		printf("\t%d", eval(sp->ini));
536 
537 	if (sp->owner)
538 		printf("\t<:struct-field:>");
539 	else
540 	if (!sp->context)
541 		printf("\t<:global:>");
542 	else
543 		printf("\t<%s>", sp->context->name);
544 
545 	if (sp->Nid < 0)	/* formal parameter */
546 		printf("\t<parameter %d>", -(sp->Nid));
547 	else if (sp->type == MTYPE)
548 		printf("\t<constant>");
549 	else if (sp->isarray)
550 		printf("\t<array>");
551 	else
552 		printf("\t<variable>");
553 
554 	if (sp->type == CHAN && sp->ini)
555 	{	int i;
556 		for (m = sp->ini->rgt, i = 0; m; m = m->rgt)
557 			i++;
558 		printf("\t%d\t", i);
559 		for (m = sp->ini->rgt; m; m = m->rgt)
560 		{	if (m->ntyp == STRUCT)
561 				printf("struct %s", m->sym->name);
562 			else
563 				(void) puttype(m->ntyp);
564 			if (m->rgt) printf("\t");
565 		}
566 	}
567 
568 	if (!old_scope_rules)
569 	{	printf("\t{scope %s}", sp->bscp);
570 	}
571 
572 	printf("\n");
573 }
574 
575 void
symdump(void)576 symdump(void)
577 {	Ordered *walk;
578 
579 	for (walk = all_names; walk; walk = walk->next)
580 		symvar(walk->entry);
581 }
582 
583 void
chname(Symbol * sp)584 chname(Symbol *sp)
585 {	printf("chan ");
586 	if (sp->context) printf("%s-", sp->context->name);
587 	if (sp->owner) printf("%s.", sp->owner->name);
588 	printf("%s", sp->name);
589 	if (sp->nel > 1 || sp->isarray == 1) printf("[%d]", sp->nel);
590 	printf("\t");
591 }
592 
593 static struct X_lkp {
594 	int typ; char *nm;
595 } xx[] = {
596 	{ 'A', "exported as run parameter" },
597 	{ 'F', "imported as proctype parameter" },
598 	{ 'L', "used as l-value in asgnmnt" },
599 	{ 'V', "used as r-value in asgnmnt" },
600 	{ 'P', "polled in receive stmnt" },
601 	{ 'R', "used as parameter in receive stmnt" },
602 	{ 'S', "used as parameter in send stmnt" },
603 	{ 'r', "received from" },
604 	{ 's', "sent to" },
605 };
606 
607 static void
chan_check(Symbol * sp)608 chan_check(Symbol *sp)
609 {	Access *a; int i, b=0, d;
610 
611 	if (verbose&1) goto report;	/* -C -g */
612 
613 	for (a = sp->access; a; a = a->lnk)
614 		if (a->typ == 'r')
615 			b |= 1;
616 		else if (a->typ == 's')
617 			b |= 2;
618 	if (b == 3 || (sp->hidden&16))	/* balanced or formal par */
619 		return;
620 report:
621 	chname(sp);
622 	for (i = d = 0; i < (int) (sizeof(xx)/sizeof(struct X_lkp)); i++)
623 	{	b = 0;
624 		for (a = sp->access; a; a = a->lnk)
625 		{	if (a->typ == xx[i].typ)
626 			{	b++;
627 		}	}
628 		if (b == 0)
629 		{	continue;
630 		}
631 		d++;
632 		printf("\n\t%s by: ", xx[i].nm);
633 		for (a = sp->access; a; a = a->lnk)
634 		  if (a->typ == xx[i].typ)
635 		  {	printf("%s", a->who->name);
636 			if (a->what) printf(" to %s", a->what->name);
637 			if (a->cnt)  printf(" par %d", a->cnt);
638 			if (--b > 0) printf(", ");
639 		  }
640 	}
641 	printf("%s\n", (!d)?"\n\tnever used under this name":"");
642 }
643 
644 void
chanaccess(void)645 chanaccess(void)
646 {	Ordered *walk;
647 	char buf[128];
648 	extern int Caccess, separate;
649 	extern short has_code;
650 
651 	for (walk = all_names; walk; walk = walk->next)
652 	{	if (!walk->entry->owner)
653 		switch (walk->entry->type) {
654 		case CHAN:
655 			if (Caccess) chan_check(walk->entry);
656 			break;
657 		case MTYPE:
658 		case BIT:
659 		case BYTE:
660 		case SHORT:
661 		case INT:
662 		case UNSIGNED:
663 			if ((walk->entry->hidden&128))	/* was: 32 */
664 				continue;
665 
666 			if (!separate
667 			&&  !walk->entry->context
668 			&&  !has_code
669 			&&   deadvar)
670 				walk->entry->hidden |= 1; /* auto-hide */
671 
672 			if (!(verbose&32) || has_code) continue;
673 
674 			printf("spin: %s:0, warning, ", Fname->name);
675 			sputtype(buf, walk->entry->type);
676 			if (walk->entry->context)
677 				printf("proctype %s",
678 					walk->entry->context->name);
679 			else
680 				printf("global");
681 			printf(", '%s%s' variable is never used (other than in print stmnts)\n",
682 				buf, walk->entry->name);
683 	}	}
684 }
685