xref: /csrg-svn/bin/test/test.c (revision 53835)
1*53835Selan /*
2*53835Selan  * The expr and test commands.
3*53835Selan  *
4*53835Selan  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
5*53835Selan  * This file is part of ash, which is distributed under the terms specified
6*53835Selan  * by the Ash General Public License.  See the file named LICENSE.
7*53835Selan  */
8*53835Selan 
9*53835Selan #define PRINT
10*53835Selan 
11*53835Selan 
12*53835Selan #include <stdio.h>
13*53835Selan #include <errno.h>
14*53835Selan #include <sys/types.h>
15*53835Selan #include <sys/stat.h>
16*53835Selan #include "operators.h"
17*53835Selan #include "extern.h"
18*53835Selan 
19*53835Selan #define STACKSIZE 12
20*53835Selan #define NESTINCR 16
21*53835Selan 
22*53835Selan /* data types */
23*53835Selan #define STRING 0
24*53835Selan #define INTEGER 1
25*53835Selan #define BOOLEAN 2
26*53835Selan 
27*53835Selan #define INITARGS(argv)	if (argv[0] == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else
28*53835Selan 
29*53835Selan #define IS_BANG(s) (s[0] == '!' && s[1] == '\0')
30*53835Selan 
31*53835Selan #define equal(s1, s2)   (strcmp(s1, s2) == 0)
32*53835Selan 
33*53835Selan 
34*53835Selan /*
35*53835Selan  * This structure hold a value.  The type keyword specifies the type of
36*53835Selan  * the value, and the union u holds the value.  The value of a boolean
37*53835Selan  * is stored in u.num (1 = TRUE, 0 = FALSE).
38*53835Selan  */
39*53835Selan 
40*53835Selan struct value {
41*53835Selan       int type;
42*53835Selan       union {
43*53835Selan 	    char *string;
44*53835Selan 	    long num;
45*53835Selan       } u;
46*53835Selan };
47*53835Selan 
48*53835Selan struct operator {
49*53835Selan       short op;			/* which operator */
50*53835Selan       short pri;		/* priority of operator */
51*53835Selan };
52*53835Selan 
53*53835Selan 
54*53835Selan struct filestat {
55*53835Selan       char *name;		/* name of file */
56*53835Selan       int rcode;		/* return code from stat */
57*53835Selan       struct stat stat;		/* status info on file */
58*53835Selan };
59*53835Selan 
60*53835Selan static int expr_is_false __P((struct value *));
61*53835Selan static void expr_operator __P((int, struct value *, struct filestat *));
62*53835Selan static int lookup_op __P((char *, char *const*));
63*53835Selan static long atol __P((const char *));
64*53835Selan static int posix_binary_op __P((char **));
65*53835Selan static int posix_unary_op __P((char **));
66*53835Selan static int int_tcheck __P((char *));
67*53835Selan 
68*53835Selan int
69*53835Selan main(argc, argv)
70*53835Selan 	int argc;
71*53835Selan 	char **argv;
72*53835Selan {
73*53835Selan       char **ap;
74*53835Selan       char *opname;
75*53835Selan       char c;
76*53835Selan       char *p;
77*53835Selan       int nest;		/* parenthises nesting */
78*53835Selan       int op;
79*53835Selan       int pri;
80*53835Selan       int skipping;
81*53835Selan       int binary;
82*53835Selan       struct operator opstack[STACKSIZE];
83*53835Selan       struct operator *opsp;
84*53835Selan       struct value valstack[STACKSIZE + 1];
85*53835Selan       struct value *valsp;
86*53835Selan       struct filestat fs;
87*53835Selan       int ret_val;
88*53835Selan 
89*53835Selan       INITARGS(argv);
90*53835Selan       if (**argv == '[') {
91*53835Selan 	    if (! equal(argv[argc - 1], "]"))
92*53835Selan 		  error("missing ]");
93*53835Selan 	    argv[argc - 1] = NULL;
94*53835Selan       }
95*53835Selan       ap = argv + 1;
96*53835Selan       fs.name = NULL;
97*53835Selan 
98*53835Selan       /*
99*53835Selan        * Test(1) implements an inherently ambiguous grammer.  In order to
100*53835Selan        * assure some degree of consistency, we special case the POSIX
101*53835Selan        * requirements to assure correct evaluation for POSIX following
102*53835Selan        * scripts.  The following special cases comply with POSIX
103*53835Selan        * P1003.2/D11.2 Section 4.62.4.
104*53835Selan        */
105*53835Selan       switch(argc - 1) {
106*53835Selan       case 0:          /* % test */
107*53835Selan #ifdef PRINT
108*53835Selan 	      printf("1\n");
109*53835Selan #endif
110*53835Selan 	      return 1;
111*53835Selan 	      break;
112*53835Selan       case 1:          /* % test arg */
113*53835Selan #ifdef PRINT
114*53835Selan 	      printf("%d\n", (*argv[1] == '\0')? 1 : 0);
115*53835Selan #endif
116*53835Selan 	      return (*argv[1] == '\0')? 1 : 0;
117*53835Selan 	      break;
118*53835Selan       case 2:          /* % test op arg */
119*53835Selan 	      opname = argv[1];
120*53835Selan 	      if (IS_BANG(opname)) {
121*53835Selan #ifdef PRINT
122*53835Selan 		      printf("%d\n", (*argv[2] == '\0')? 1 : 0);
123*53835Selan #endif
124*53835Selan 		      return (*argv[2] == '\0')? 1 : 0;
125*53835Selan 	      } else {
126*53835Selan 		      ret_val = posix_unary_op(&argv[1]);
127*53835Selan 		      if (ret_val >= 0) {
128*53835Selan #ifdef PRINT
129*53835Selan 			      printf("%d\n", ret_val);
130*53835Selan #endif
131*53835Selan 			      return ret_val;
132*53835Selan 		      }
133*53835Selan 	      }
134*53835Selan 	      break;
135*53835Selan       case 3:         /* % test arg1 op arg2 */
136*53835Selan 	      if (IS_BANG(argv[1])) {
137*53835Selan 		      ret_val = posix_unary_op(&argv[1]);
138*53835Selan 		      if (ret_val >= 0) {
139*53835Selan #ifdef PRINT
140*53835Selan 			      printf("%d\n", !ret_val);
141*53835Selan #endif
142*53835Selan 			      return !ret_val;
143*53835Selan 		      }
144*53835Selan 	      } else {
145*53835Selan 		      ret_val = posix_binary_op(&argv[1]);
146*53835Selan 		      if (ret_val >= 0) {
147*53835Selan #ifdef PRINT
148*53835Selan 			      printf("%d\n", ret_val);
149*53835Selan #endif
150*53835Selan  			      return ret_val;
151*53835Selan 		      }
152*53835Selan 	      }
153*53835Selan 	      break;
154*53835Selan       case 4:         /* % test ! arg1 op arg2 */
155*53835Selan 	      if (IS_BANG(argv[1])) {
156*53835Selan 		      ret_val = posix_binary_op(&argv[2]);
157*53835Selan 		      if (ret_val >= 0) {
158*53835Selan #ifdef PRINT
159*53835Selan 			      printf("%d\n", !ret_val);
160*53835Selan #endif
161*53835Selan  			      return !ret_val;
162*53835Selan 		      }
163*53835Selan 	      }
164*53835Selan 	      break;
165*53835Selan       default:
166*53835Selan 	      break;
167*53835Selan       }
168*53835Selan 
169*53835Selan       /*
170*53835Selan        * We use operator precedence parsing, evaluating the expression
171*53835Selan        * as we parse it.  Parentheses are handled by bumping up the
172*53835Selan        * priority of operators using the variable "nest."  We use the
173*53835Selan        * variable "skipping" to turn off evaluation temporarily for the
174*53835Selan        * short circuit boolean operators.  (It is important do the short
175*53835Selan        * circuit evaluation because under NFS a stat operation can take
176*53835Selan        * infinitely long.)
177*53835Selan        */
178*53835Selan 
179*53835Selan       opsp = opstack + STACKSIZE;
180*53835Selan       valsp = valstack;
181*53835Selan       nest = 0;
182*53835Selan       skipping = 0;
183*53835Selan       if (*ap == NULL) {
184*53835Selan 	    valstack[0].type = BOOLEAN;
185*53835Selan 	    valstack[0].u.num = 0;
186*53835Selan 	    goto done;
187*53835Selan       }
188*53835Selan       for (;;) {
189*53835Selan 	    opname = *ap++;
190*53835Selan 	    if (opname == NULL)
191*53835Selan 		  goto syntax;
192*53835Selan 	    if (opname[0] == '(' && opname[1] == '\0') {
193*53835Selan 		  nest += NESTINCR;
194*53835Selan 		  continue;
195*53835Selan 	    } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
196*53835Selan 		  if (opsp == &opstack[0])
197*53835Selan 			  goto overflow;
198*53835Selan 		  --opsp;
199*53835Selan 		  opsp->op = op;
200*53835Selan 		  opsp->pri = op_priority[op] + nest;
201*53835Selan 		  continue;
202*53835Selan 
203*53835Selan 	    } else {
204*53835Selan 		  valsp->type = STRING;
205*53835Selan 		  valsp->u.string = opname;
206*53835Selan 		  valsp++;
207*53835Selan 	    }
208*53835Selan 	    for (;;) {
209*53835Selan 		  opname = *ap++;
210*53835Selan 		  if (opname == NULL) {
211*53835Selan 			if (nest != 0)
212*53835Selan 			      goto syntax;
213*53835Selan 			pri = 0;
214*53835Selan 			break;
215*53835Selan 		  }
216*53835Selan 		  if (opname[0] != ')' || opname[1] != '\0') {
217*53835Selan 			if ((op = lookup_op(opname, binary_op)) < 0)
218*53835Selan 			      goto syntax;
219*53835Selan 			op += FIRST_BINARY_OP;
220*53835Selan 			pri = op_priority[op] + nest;
221*53835Selan 			break;
222*53835Selan 		  }
223*53835Selan 		  if ((nest -= NESTINCR) < 0)
224*53835Selan 			goto syntax;
225*53835Selan 	    }
226*53835Selan 	    while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
227*53835Selan 		  binary = opsp->op;
228*53835Selan 		  for (;;) {
229*53835Selan 			valsp--;
230*53835Selan 			c = op_argflag[opsp->op];
231*53835Selan 			if (c == OP_INT) {
232*53835Selan 			      if (valsp->type == STRING &&
233*53835Selan 				  int_tcheck(valsp->u.string))
234*53835Selan 					      valsp->u.num =
235*53835Selan 					          atol(valsp->u.string);
236*53835Selan 			      valsp->type = INTEGER;
237*53835Selan 			} else if (c >= OP_STRING) { /* OP_STRING or OP_FILE */
238*53835Selan 			      if (valsp->type == INTEGER) {
239*53835Selan 				    p = stalloc(32);
240*53835Selan #ifdef SHELL
241*53835Selan 				    fmtstr(p, 32, "%d", valsp->u.num);
242*53835Selan #else
243*53835Selan 				    sprintf(p, "%d", valsp->u.num);
244*53835Selan #endif
245*53835Selan 				    valsp->u.string = p;
246*53835Selan 			      } else if (valsp->type == BOOLEAN) {
247*53835Selan 				    if (valsp->u.num)
248*53835Selan 					  valsp->u.string = "true";
249*53835Selan 				    else
250*53835Selan 					  valsp->u.string = "";
251*53835Selan 			      }
252*53835Selan 			      valsp->type = STRING;
253*53835Selan 			      if (c == OP_FILE
254*53835Selan 			       && (fs.name == NULL
255*53835Selan 			           || ! equal(fs.name, valsp->u.string))) {
256*53835Selan 				    fs.name = valsp->u.string;
257*53835Selan 				    fs.rcode = stat(valsp->u.string, &fs.stat);
258*53835Selan 			      }
259*53835Selan 			}
260*53835Selan 			if (binary < FIRST_BINARY_OP)
261*53835Selan 			      break;
262*53835Selan 			binary = 0;
263*53835Selan 		  }
264*53835Selan 		  if (! skipping)
265*53835Selan 			expr_operator(opsp->op, valsp, &fs);
266*53835Selan 		  else if (opsp->op == AND1 || opsp->op == OR1)
267*53835Selan 			skipping--;
268*53835Selan 		  valsp++;		/* push value */
269*53835Selan 		  opsp++;		/* pop operator */
270*53835Selan 	    }
271*53835Selan 	    if (opname == NULL)
272*53835Selan 		  break;
273*53835Selan 	    if (opsp == &opstack[0])
274*53835Selan 		  goto overflow;
275*53835Selan 	    if (op == AND1 || op == AND2) {
276*53835Selan 		  op = AND1;
277*53835Selan 		  if (skipping || expr_is_false(valsp - 1))
278*53835Selan 			skipping++;
279*53835Selan 	    }
280*53835Selan 	    if (op == OR1 || op == OR2) {
281*53835Selan 		  op = OR1;
282*53835Selan 		  if (skipping || ! expr_is_false(valsp - 1))
283*53835Selan 			skipping++;
284*53835Selan 	    }
285*53835Selan 	    opsp--;
286*53835Selan 	    opsp->op = op;
287*53835Selan 	    opsp->pri = pri;
288*53835Selan       }
289*53835Selan done:
290*53835Selan #ifdef PRINT
291*53835Selan 	    if (valstack[0].type == STRING)
292*53835Selan 		  printf("%s\n", valstack[0].u.string);
293*53835Selan 	    else if (valstack[0].type == INTEGER)
294*53835Selan 		  printf("%ld\n", valstack[0].u.num);
295*53835Selan 
296*53835Selan 	    else if (valstack[0].u.num != 0)
297*53835Selan 		  printf("true\n");
298*53835Selan #endif
299*53835Selan 
300*53835Selan       return expr_is_false(&valstack[0]);
301*53835Selan 
302*53835Selan  syntax:   error("syntax error");
303*53835Selan  overflow: error("Expression too complex");
304*53835Selan 
305*53835Selan }
306*53835Selan 
307*53835Selan 
308*53835Selan static int
309*53835Selan expr_is_false(val)
310*53835Selan       struct value *val;
311*53835Selan {
312*53835Selan       if (val->type == STRING) {
313*53835Selan 	    if (val->u.string[0] == '\0')
314*53835Selan 		  return 1;
315*53835Selan       } else {	/* INTEGER or BOOLEAN */
316*53835Selan 	    if (val->u.num == 0)
317*53835Selan 		  return 1;
318*53835Selan       }
319*53835Selan       return 0;
320*53835Selan }
321*53835Selan 
322*53835Selan 
323*53835Selan /*
324*53835Selan  * Execute an operator.  Op is the operator.  Sp is the stack pointer;
325*53835Selan  * sp[0] refers to the first operand, sp[1] refers to the second operand
326*53835Selan  * (if any), and the result is placed in sp[0].  The operands are converted
327*53835Selan  * to the type expected by the operator before expr_operator is called.
328*53835Selan  * Fs is a pointer to a structure which holds the value of the last call
329*53835Selan  * to stat, to avoid repeated stat calls on the same file.
330*53835Selan  */
331*53835Selan 
332*53835Selan static void
333*53835Selan expr_operator(op, sp, fs)
334*53835Selan 	int op;
335*53835Selan 	struct value *sp;
336*53835Selan 	struct filestat *fs;
337*53835Selan {
338*53835Selan 	int i;
339*53835Selan 
340*53835Selan 	switch (op) {
341*53835Selan 	case NOT:
342*53835Selan 	    sp->u.num = expr_is_false(sp);
343*53835Selan 	    sp->type = BOOLEAN;
344*53835Selan 	    break;
345*53835Selan     case ISEXIST:
346*53835Selan 	    if (fs == NULL || (fs->rcode == -1 && errno == ENOENT))
347*53835Selan 		    goto false;
348*53835Selan 	    else
349*53835Selan 		    goto true;
350*53835Selan     case ISREAD:
351*53835Selan 	    i = 04;
352*53835Selan 	    goto permission;
353*53835Selan     case ISWRITE:
354*53835Selan 	    i = 02;
355*53835Selan 	    goto permission;
356*53835Selan     case ISEXEC:
357*53835Selan 	    i = 01;
358*53835Selan permission:
359*53835Selan 	    if (fs->stat.st_uid == geteuid())
360*53835Selan 		  i <<= 6;
361*53835Selan 	    else if (fs->stat.st_gid == getegid())
362*53835Selan 		  i <<= 3;
363*53835Selan 	    goto filebit;	/* true if (stat.st_mode & i) != 0 */
364*53835Selan     case ISFILE:
365*53835Selan 	    i = S_IFREG;
366*53835Selan 	    goto filetype;
367*53835Selan     case ISDIR:
368*53835Selan 	    i = S_IFDIR;
369*53835Selan 	    goto filetype;
370*53835Selan     case ISCHAR:
371*53835Selan 	    i = S_IFCHR;
372*53835Selan 	    goto filetype;
373*53835Selan     case ISBLOCK:
374*53835Selan 	    i = S_IFBLK;
375*53835Selan 	    goto filetype;
376*53835Selan     case ISFIFO:
377*53835Selan #ifdef S_IFIFO
378*53835Selan 	    i = S_IFIFO;
379*53835Selan 	    goto filetype;
380*53835Selan #else
381*53835Selan 	    goto false;
382*53835Selan #endif
383*53835Selan filetype:
384*53835Selan 	    if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) {
385*53835Selan true:
386*53835Selan 		  sp->u.num = 1;
387*53835Selan 	    } else {
388*53835Selan false:
389*53835Selan 		  sp->u.num = 0;
390*53835Selan 	    }
391*53835Selan 	    sp->type = BOOLEAN;
392*53835Selan 	    break;
393*53835Selan     case ISSETUID:
394*53835Selan 	    i = S_ISUID;
395*53835Selan 	    goto filebit;
396*53835Selan     case ISSETGID:
397*53835Selan 	    i = S_ISGID;
398*53835Selan 	    goto filebit;
399*53835Selan     case ISSTICKY:
400*53835Selan 	    i = S_ISVTX;
401*53835Selan filebit:
402*53835Selan 	    if (fs->stat.st_mode & i && fs->rcode >= 0)
403*53835Selan 		  goto true;
404*53835Selan 	    goto false;
405*53835Selan     case ISSIZE:
406*53835Selan 	    sp->u.num = fs->rcode >= 0? fs->stat.st_size : 0L;
407*53835Selan 	    sp->type = INTEGER;
408*53835Selan 	    break;
409*53835Selan     case ISTTY:
410*53835Selan 	    sp->u.num = isatty(sp->u.num);
411*53835Selan 	    sp->type = BOOLEAN;
412*53835Selan 	    break;
413*53835Selan     case NULSTR:
414*53835Selan 	    if (sp->u.string[0] == '\0')
415*53835Selan 		  goto true;
416*53835Selan 	    goto false;
417*53835Selan     case STRLEN:
418*53835Selan 	    sp->u.num = strlen(sp->u.string);
419*53835Selan 	    sp->type = INTEGER;
420*53835Selan 	    break;
421*53835Selan     case OR1:
422*53835Selan     case AND1:
423*53835Selan 	    /*
424*53835Selan 	     * These operators are mostly handled by the parser.  If we
425*53835Selan 	     * get here it means that both operands were evaluated, so
426*53835Selan 	     * the value is the value of the second operand.
427*53835Selan 	     */
428*53835Selan 	    *sp = *(sp + 1);
429*53835Selan 	    break;
430*53835Selan     case STREQ:
431*53835Selan     case STRNE:
432*53835Selan 	    i = 0;
433*53835Selan 	    if (equal(sp->u.string, (sp + 1)->u.string))
434*53835Selan 		  i++;
435*53835Selan 	    if (op == STRNE)
436*53835Selan 		  i = 1 - i;
437*53835Selan 	    sp->u.num = i;
438*53835Selan 	    sp->type = BOOLEAN;
439*53835Selan 	    break;
440*53835Selan     case EQ:
441*53835Selan 	    if (sp->u.num == (sp + 1)->u.num)
442*53835Selan 		  goto true;
443*53835Selan 	    goto false;
444*53835Selan     case NE:
445*53835Selan 	    if (sp->u.num != (sp + 1)->u.num)
446*53835Selan 		  goto true;
447*53835Selan 	    goto false;
448*53835Selan     case GT:
449*53835Selan 	    if (sp->u.num > (sp + 1)->u.num)
450*53835Selan 		  goto true;
451*53835Selan 	    goto false;
452*53835Selan     case LT:
453*53835Selan 	    if (sp->u.num < (sp + 1)->u.num)
454*53835Selan 		  goto true;
455*53835Selan 	    goto false;
456*53835Selan     case LE:
457*53835Selan 	    if (sp->u.num <= (sp + 1)->u.num)
458*53835Selan 		  goto true;
459*53835Selan 	    goto false;
460*53835Selan     case GE:
461*53835Selan 	    if (sp->u.num >= (sp + 1)->u.num)
462*53835Selan 		  goto true;
463*53835Selan 	    goto false;
464*53835Selan 
465*53835Selan       }
466*53835Selan }
467*53835Selan 
468*53835Selan 
469*53835Selan static int
470*53835Selan lookup_op(name, table)
471*53835Selan       char *name;
472*53835Selan       char *const*table;
473*53835Selan {
474*53835Selan       register char *const*tp;
475*53835Selan       register char const *p;
476*53835Selan       char c = name[1];
477*53835Selan 
478*53835Selan       for (tp = table ; (p = *tp) != NULL ; tp++) {
479*53835Selan 	    if (p[1] == c && equal(p, name))
480*53835Selan 		  return tp - table;
481*53835Selan       }
482*53835Selan       return -1;
483*53835Selan }
484*53835Selan 
485*53835Selan static int
486*53835Selan posix_unary_op(argv)
487*53835Selan 	char **argv;
488*53835Selan {
489*53835Selan 	int op, c;
490*53835Selan 	char *opname;
491*53835Selan 	struct filestat fs;
492*53835Selan 	struct value valp;
493*53835Selan 
494*53835Selan 	opname = *argv;
495*53835Selan         if ((op = lookup_op(opname, unary_op)) < 0)
496*53835Selan 		return -1;
497*53835Selan         c = op_argflag[op];
498*53835Selan         opname = argv[1];
499*53835Selan         valp.u.string = opname;
500*53835Selan         if (c == OP_FILE) {
501*53835Selan 		fs.name = opname;
502*53835Selan 		fs.rcode = stat(opname, &fs.stat);
503*53835Selan 	} else if (c != OP_STRING)
504*53835Selan 		return -1;
505*53835Selan 
506*53835Selan 	expr_operator(op, &valp, &fs);
507*53835Selan 	return (valp.u.num == 0);
508*53835Selan }
509*53835Selan 
510*53835Selan 
511*53835Selan static int
512*53835Selan posix_binary_op(argv)
513*53835Selan 	char **argv;
514*53835Selan {
515*53835Selan 	int op, c;
516*53835Selan 	char *opname;
517*53835Selan 	struct value v[2];
518*53835Selan 
519*53835Selan 	opname = argv[1];
520*53835Selan 	if ((op = lookup_op(opname, binary_op)) < 0)
521*53835Selan 		return -1;
522*53835Selan 	op += FIRST_BINARY_OP;
523*53835Selan 	c = op_argflag[op];
524*53835Selan 
525*53835Selan 	if (c == OP_INT && int_tcheck(argv[0]) && int_tcheck(argv[2])) {
526*53835Selan 		v[0].u.num = atol(argv[0]);
527*53835Selan 		v[1].u.num = atol(argv[2]);
528*53835Selan 	}
529*53835Selan 	expr_operator(op, v, NULL);
530*53835Selan 	return (v[0].u.num == 0);
531*53835Selan }
532*53835Selan 
533*53835Selan /*
534*53835Selan  * Integer type checking.
535*53835Selan  */
536*53835Selan static int
537*53835Selan int_tcheck(v)
538*53835Selan 	char *v;
539*53835Selan {
540*53835Selan 	char *p;
541*53835Selan 	char outbuf[512];
542*53835Selan 
543*53835Selan 	for (p = v; *p != '\0'; p++)
544*53835Selan 		if (*p < '0' || *p > '9') {
545*53835Selan 			snprintf(outbuf, sizeof(outbuf),
546*53835Selan 			    "Illegal operand \"%s\" -- expected integer.", v);
547*53835Selan 
548*53835Selan 			error(outbuf);
549*53835Selan 		}
550*53835Selan 	return 1;
551*53835Selan }
552*53835Selan 
553*53835Selan 
554*53835Selan 
555*53835Selan 
556*53835Selan 
557*53835Selan 
558*53835Selan 
559*53835Selan 
560*53835Selan 
561*53835Selan 
562