1 /*
2 * sh.exp.c: Expression evaluations
3 */
4 /*-
5 * Copyright (c) 1980, 1991 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32 #include "sh.h"
33 #include "tw.h"
34
35 /*
36 * C shell
37 */
38
39 #define TEXP_IGNORE 1 /* in ignore, it means to ignore value, just parse */
40 #define TEXP_NOGLOB 2 /* in ignore, it means not to globone */
41
42 #define ADDOP 1
43 #define MULOP 2
44 #define EQOP 4
45 #define RELOP 8
46 #define RESTOP 16
47 #define ANYOP 31
48
49 #define EQEQ 1
50 #define GTR 2
51 #define LSS 4
52 #define NOTEQ 6
53 #define EQMATCH 7
54 #define NOTEQMATCH 8
55
56 static int sh_access (const Char *, int);
57 static tcsh_number_t exp1 (Char ***, int);
58 static tcsh_number_t exp2x (Char ***, int);
59 static tcsh_number_t exp2a (Char ***, int);
60 static tcsh_number_t exp2b (Char ***, int);
61 static tcsh_number_t exp2c (Char ***, int);
62 static Char *exp3 (Char ***, int);
63 static Char *exp3a (Char ***, int);
64 static Char *exp4 (Char ***, int);
65 static Char *exp5 (Char ***, int);
66 static Char *exp6 (Char ***, int);
67 static void evalav (Char **);
68 static int isa (Char *, int);
69 static tcsh_number_t egetn (const Char *);
70
71 #ifdef EDEBUG
72 static void etracc (const char *, const Char *, Char ***);
73 static void etraci (const char *, tcsh_number_t, Char ***);
74 #else /* !EDEBUG */
75 #define etracc(A, B, C) ((void)0)
76 #define etraci(A, B, C) ((void)0)
77 #endif /* !EDEBUG */
78
79 /*
80 * shell access function according to POSIX and non POSIX
81 * From Beto Appleton (beto@aixwiz.aix.ibm.com)
82 */
83 static int
sh_access(const Char * fname,int mode)84 sh_access(const Char *fname, int mode)
85 {
86 #if defined(POSIX) && !defined(USE_ACCESS)
87 struct stat statb;
88 #endif /* POSIX */
89 char *name = short2str(fname);
90
91 if (*name == '\0')
92 return 1;
93
94 #if !defined(POSIX) || defined(USE_ACCESS)
95 return access(name, mode);
96 #else /* POSIX */
97
98 /*
99 * POSIX 1003.2-d11.2
100 * -r file True if file exists and is readable.
101 * -w file True if file exists and is writable.
102 * True shall indicate only that the write flag is on.
103 * The file shall not be writable on a read-only file
104 * system even if this test indicates true.
105 * -x file True if file exists and is executable.
106 * True shall indicate only that the execute flag is on.
107 * If file is a directory, true indicates that the file
108 * can be searched.
109 */
110 if (mode != W_OK && mode != X_OK)
111 return access(name, mode);
112
113 if (stat(name, &statb) == -1)
114 return 1;
115
116 if (access(name, mode) == 0) {
117 #ifdef S_ISDIR
118 if (S_ISDIR(statb.st_mode) && mode == X_OK)
119 return 0;
120 #endif /* S_ISDIR */
121
122 /* root needs permission for someone */
123 switch (mode) {
124 case W_OK:
125 mode = S_IWUSR | S_IWGRP | S_IWOTH;
126 break;
127 case X_OK:
128 mode = S_IXUSR | S_IXGRP | S_IXOTH;
129 break;
130 default:
131 abort();
132 break;
133 }
134
135 }
136
137 else if (euid == statb.st_uid)
138 mode <<= 6;
139
140 else if (egid == statb.st_gid)
141 mode <<= 3;
142
143 # ifdef NGROUPS_MAX
144 else {
145 /* you can be in several groups */
146 long n;
147 GETGROUPS_T *groups;
148
149 /*
150 * Try these things to find a positive maximum groups value:
151 * 1) sysconf(_SC_NGROUPS_MAX)
152 * 2) NGROUPS_MAX
153 * 3) getgroups(0, unused)
154 * Then allocate and scan the groups array if one of these worked.
155 */
156 # if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX)
157 if ((n = sysconf(_SC_NGROUPS_MAX)) == -1)
158 # endif /* _SC_NGROUPS_MAX */
159 n = NGROUPS_MAX;
160 if (n <= 0)
161 n = getgroups(0, (GETGROUPS_T *) NULL);
162
163 if (n > 0) {
164 groups = xmalloc(n * sizeof(*groups));
165 n = getgroups((int) n, groups);
166 while (--n >= 0)
167 if (groups[n] == statb.st_gid) {
168 mode <<= 3;
169 break;
170 }
171 xfree(groups);
172 }
173 }
174 # endif /* NGROUPS_MAX */
175
176 if (statb.st_mode & mode)
177 return 0;
178 else
179 return 1;
180 #endif /* !POSIX */
181 }
182
183 tcsh_number_t
expr(Char *** vp)184 expr(Char ***vp)
185 {
186 return (exp0(vp, 0));
187 }
188
189 tcsh_number_t
exp0(Char *** vp,int ignore)190 exp0(Char ***vp, int ignore)
191 {
192 tcsh_number_t p1 = exp1(vp, ignore);
193
194 etraci("exp0 p1", p1, vp);
195 while (**vp && eq(**vp, STRor2)) {
196 int p2;
197
198 (*vp)++;
199
200 p2 = compat_expr ?
201 exp0(vp, (ignore & TEXP_IGNORE) || p1) :
202 exp1(vp, (ignore & TEXP_IGNORE) || p1);
203 if (compat_expr || !(ignore & TEXP_IGNORE))
204 p1 = (p1 || p2);
205 etraci("exp0 p1", p1, vp);
206 if (compat_expr)
207 break;
208 }
209 return (p1);
210 }
211
212 static tcsh_number_t
exp1(Char *** vp,int ignore)213 exp1(Char ***vp, int ignore)
214 {
215 tcsh_number_t p1 = exp2x(vp, ignore);
216
217 etraci("exp1 p1", p1, vp);
218 while (**vp && eq(**vp, STRand2)) {
219 tcsh_number_t p2;
220
221 (*vp)++;
222 p2 = compat_expr ?
223 exp1(vp, (ignore & TEXP_IGNORE) || !p1) :
224 exp2x(vp, (ignore & TEXP_IGNORE) || !p1);
225
226 etraci("exp1 p2", p2, vp);
227 if (compat_expr || !(ignore & TEXP_IGNORE))
228 p1 = (p1 && p2);
229 etraci("exp1 p1", p1, vp);
230 if (compat_expr)
231 break;
232 }
233 return (p1);
234 }
235
236 static tcsh_number_t
exp2x(Char *** vp,int ignore)237 exp2x(Char ***vp, int ignore)
238 {
239 tcsh_number_t p1 = exp2a(vp, ignore);
240
241 etraci("exp2x p1", p1, vp);
242 while (**vp && eq(**vp, STRor)) {
243 tcsh_number_t p2;
244
245 (*vp)++;
246 p2 = compat_expr ?
247 exp2x(vp, ignore) :
248 exp2a(vp, ignore);
249 etraci("exp2x p2", p2, vp);
250 if (compat_expr || !(ignore & TEXP_IGNORE))
251 p1 = (p1 | p2);
252 etraci("exp2x p1", p1, vp);
253 if (compat_expr)
254 break;
255 }
256 return (p1);
257 }
258
259 static tcsh_number_t
exp2a(Char *** vp,int ignore)260 exp2a(Char ***vp, int ignore)
261 {
262 tcsh_number_t p1 = exp2b(vp, ignore);
263
264 etraci("exp2a p1", p1, vp);
265 while (**vp && eq(**vp, STRcaret)) {
266 tcsh_number_t p2;
267
268 (*vp)++;
269 p2 = compat_expr ?
270 exp2a(vp, ignore) :
271 exp2b(vp, ignore);
272 etraci("exp2a p2", p2, vp);
273 if (compat_expr || !(ignore & TEXP_IGNORE))
274 p1 = (p1 ^ p2);
275 etraci("exp2a p1", p1, vp);
276 if (compat_expr)
277 break;
278 }
279 return (p1);
280 }
281
282 static tcsh_number_t
exp2b(Char *** vp,int ignore)283 exp2b(Char ***vp, int ignore)
284 {
285 tcsh_number_t p1 = exp2c(vp, ignore);
286
287 etraci("exp2b p1", p1, vp);
288 while (**vp && eq(**vp, STRand)) {
289 tcsh_number_t p2;
290
291 (*vp)++;
292 p2 = compat_expr ?
293 exp2b(vp, ignore) :
294 exp2c(vp, ignore);
295 etraci("exp2b p2", p2, vp);
296 if (compat_expr || !(ignore & TEXP_IGNORE))
297 p1 = (p1 & p2);
298 etraci("exp2b p1", p1, vp);
299 if (compat_expr)
300 break;
301 }
302 return (p1);
303 }
304
305 static tcsh_number_t
exp2c(Char *** vp,int ignore)306 exp2c(Char ***vp, int ignore)
307 {
308 Char *p1 = exp3(vp, ignore);
309 Char *p2;
310 tcsh_number_t i;
311
312 cleanup_push(p1, xfree);
313 etracc("exp2c p1", p1, vp);
314 if ((i = isa(**vp, EQOP)) != 0) {
315 (*vp)++;
316 if (i == EQMATCH || i == NOTEQMATCH)
317 ignore |= TEXP_NOGLOB;
318 p2 = exp3(vp, ignore);
319 cleanup_push(p2, xfree);
320 etracc("exp2c p2", p2, vp);
321 if (!(ignore & TEXP_IGNORE))
322 switch ((int)i) {
323
324 case EQEQ:
325 i = eq(p1, p2);
326 break;
327
328 case NOTEQ:
329 i = !eq(p1, p2);
330 break;
331
332 case EQMATCH:
333 i = Gmatch(p1, p2);
334 break;
335
336 case NOTEQMATCH:
337 i = !Gmatch(p1, p2);
338 break;
339 }
340 cleanup_until(p1);
341 return (i);
342 }
343 i = egetn(p1);
344 cleanup_until(p1);
345 return (i);
346 }
347
348 static Char *
exp3(Char *** vp,int ignore)349 exp3(Char ***vp, int ignore)
350 {
351 Char *p1, *p2;
352 tcsh_number_t i;
353
354 p1 = exp3a(vp, ignore);
355 etracc("exp3 p1", p1, vp);
356 while ((i = isa(**vp, RELOP)) != 0) {
357 (*vp)++;
358 if (**vp && eq(**vp, STRequal))
359 i |= 1, (*vp)++;
360 cleanup_push(p1, xfree);
361 p2 = compat_expr ?
362 exp3(vp, ignore) :
363 exp3a(vp, ignore);
364 cleanup_push(p2, xfree);
365 etracc("exp3 p2", p2, vp);
366 if (!(ignore & TEXP_IGNORE))
367 switch ((int)i) {
368
369 case GTR:
370 i = egetn(p1) > egetn(p2);
371 break;
372
373 case GTR | 1:
374 i = egetn(p1) >= egetn(p2);
375 break;
376
377 case LSS:
378 i = egetn(p1) < egetn(p2);
379 break;
380
381 case LSS | 1:
382 i = egetn(p1) <= egetn(p2);
383 break;
384 }
385 cleanup_until(p1);
386 p1 = putn(i);
387 etracc("exp3 p1", p1, vp);
388 if (compat_expr)
389 break;
390 }
391 return (p1);
392 }
393
394 static Char *
exp3a(Char *** vp,int ignore)395 exp3a(Char ***vp, int ignore)
396 {
397 Char *p1, *p2;
398 const Char *op;
399 tcsh_number_t i;
400
401 p1 = exp4(vp, ignore);
402 etracc("exp3a p1", p1, vp);
403 op = **vp;
404 if (op && any("<>", op[0]) && op[0] == op[1]) {
405 (*vp)++;
406 cleanup_push(p1, xfree);
407 p2 = compat_expr ?
408 exp3a(vp, ignore) :
409 exp4(vp, ignore);
410 cleanup_push(p2, xfree);
411 etracc("exp3a p2", p2, vp);
412 if (op[0] == '<')
413 i = egetn(p1) << egetn(p2);
414 else
415 i = egetn(p1) >> egetn(p2);
416 cleanup_until(p1);
417 p1 = putn(i);
418 etracc("exp3a p1", p1, vp);
419 }
420 return (p1);
421 }
422
423 static Char *
exp4(Char *** vp,int ignore)424 exp4(Char ***vp, int ignore)
425 {
426 Char *p1, *p2;
427 tcsh_number_t i = 0;
428
429 p1 = exp5(vp, ignore);
430 etracc("exp4 p1", p1, vp);
431 while (isa(**vp, ADDOP)) {
432 const Char *op = *(*vp)++;
433
434 cleanup_push(p1, xfree);
435 p2 = compat_expr ?
436 exp4(vp, ignore) :
437 exp5(vp, ignore);
438 cleanup_push(p2, xfree);
439 etracc("exp4 p2", p2, vp);
440 if (!(ignore & TEXP_IGNORE))
441 switch (op[0]) {
442
443 case '+':
444 i = egetn(p1) + egetn(p2);
445 break;
446
447 case '-':
448 i = egetn(p1) - egetn(p2);
449 break;
450 }
451 cleanup_until(p1);
452 p1 = putn(i);
453 etracc("exp4 p1", p1, vp);
454 if (compat_expr)
455 break;
456 }
457 return (p1);
458 }
459
460 static Char *
exp5(Char *** vp,int ignore)461 exp5(Char ***vp, int ignore)
462 {
463 Char *p1, *p2;
464 tcsh_number_t i = 0;
465
466 p1 = exp6(vp, ignore);
467 etracc("exp5 p1", p1, vp);
468
469 while (isa(**vp, MULOP)) {
470 const Char *op = *(*vp)++;
471 if ((ignore & TEXP_NOGLOB) != 0) {
472 /*
473 * We are just trying to get the right side of
474 * a =~ or !~ operator
475 */
476 xfree(p1);
477 return Strsave(op);
478 }
479
480 cleanup_push(p1, xfree);
481 p2 = compat_expr ?
482 exp5(vp, ignore) :
483 exp6(vp, ignore);
484 cleanup_push(p2, xfree);
485 etracc("exp5 p2", p2, vp);
486 if (!(ignore & TEXP_IGNORE))
487 switch (op[0]) {
488
489 case '*':
490 i = egetn(p1) * egetn(p2);
491 break;
492
493 case '/':
494 i = egetn(p2);
495 if (i == 0)
496 stderror(ERR_DIV0);
497 i = egetn(p1) / i;
498 break;
499
500 case '%':
501 i = egetn(p2);
502 if (i == 0)
503 stderror(ERR_MOD0);
504 i = egetn(p1) % i;
505 break;
506 }
507 cleanup_until(p1);
508 p1 = putn(i);
509 etracc("exp5 p1", p1, vp);
510 if (compat_expr)
511 break;
512 }
513 return (p1);
514 }
515
516 static Char *
exp6(Char *** vp,int ignore)517 exp6(Char ***vp, int ignore)
518 {
519 tcsh_number_t ccode;
520 tcsh_number_t i = 0;
521 Char *cp;
522
523 if (**vp == 0)
524 stderror(ERR_NAME | ERR_EXPRESSION);
525 if (eq(**vp, STRbang)) {
526 (*vp)++;
527 cp = exp6(vp, ignore);
528 cleanup_push(cp, xfree);
529 etracc("exp6 ! cp", cp, vp);
530 i = egetn(cp);
531 cleanup_until(cp);
532 return (putn(!i));
533 }
534 if (eq(**vp, STRtilde)) {
535 (*vp)++;
536 cp = exp6(vp, ignore);
537 cleanup_push(cp, xfree);
538 etracc("exp6 ~ cp", cp, vp);
539 i = egetn(cp);
540 cleanup_until(cp);
541 return (putn(~i));
542 }
543 if (eq(**vp, STRLparen)) {
544 (*vp)++;
545 ccode = exp0(vp, ignore);
546 etraci("exp6 () ccode", ccode, vp);
547 if (**vp == 0 || ***vp != ')')
548 stderror(ERR_NAME | ERR_EXPRESSION);
549 (*vp)++;
550 return (putn(ccode));
551 }
552 if (eq(**vp, STRLbrace)) {
553 Char **v;
554 struct command faket;
555 Char *fakecom[2];
556
557 faket.t_dtyp = NODE_COMMAND;
558 faket.t_dflg = F_BACKQ;
559 faket.t_dcar = faket.t_dcdr = faket.t_dspr = NULL;
560 faket.t_dcom = fakecom;
561 fakecom[0] = STRfakecom;
562 fakecom[1] = NULL;
563 (*vp)++;
564 v = *vp;
565 for (;;) {
566 if (!**vp)
567 stderror(ERR_NAME | ERR_MISSING, '}');
568 if (eq(*(*vp)++, STRRbrace))
569 break;
570 }
571 if (ignore & TEXP_IGNORE)
572 return (Strsave(STRNULL));
573 psavejob();
574 cleanup_push(&faket, psavejob_cleanup); /* faket is only a marker */
575 if (pfork(&faket, -1) == 0) {
576 *--(*vp) = 0;
577 evalav(v);
578 exitstat();
579 }
580 pwait();
581 cleanup_until(&faket);
582 etraci("exp6 {} status", egetn(varval(STRstatus)), vp);
583 return (putn(egetn(varval(STRstatus)) == 0));
584 }
585 if (isa(**vp, ANYOP))
586 return (Strsave(STRNULL));
587 cp = *(*vp)++;
588 #ifdef convex
589 # define FILETESTS "erwxfdzoplstSXLbcugkmKR"
590 #else
591 # define FILETESTS "erwxfdzoplstSXLbcugkmK"
592 #endif /* convex */
593 #define FILEVALS "ZAMCDIUGNFPL"
594 if (*cp == '-' && (any(FILETESTS, cp[1]) || any(FILEVALS, cp[1])))
595 return(filetest(cp, vp, ignore));
596 etracc("exp6 default", cp, vp);
597 return (ignore & TEXP_NOGLOB ? Strsave(cp) : globone(cp, G_APPEND));
598 }
599
600
601 /*
602 * Extended file tests
603 * From: John Rowe <rowe@excc.exeter.ac.uk>
604 */
605 Char *
filetest(Char * cp,Char *** vp,int ignore)606 filetest(Char *cp, Char ***vp, int ignore)
607 {
608 #ifdef convex
609 struct cvxstat stb, *st = NULL;
610 # define TCSH_STAT stat64
611 #else
612 # define TCSH_STAT stat
613 struct stat stb, *st = NULL;
614 #endif /* convex */
615
616 #ifdef S_IFLNK
617 # ifdef convex
618 struct cvxstat lstb, *lst = NULL;
619 # define TCSH_LSTAT lstat64
620 # else
621 # define TCSH_LSTAT lstat
622 struct stat lstb, *lst = NULL;
623 # endif /* convex */
624 char *filnam;
625 #endif /* S_IFLNK */
626
627 tcsh_number_t i = 0;
628 unsigned pmask = 0xffff;
629 int altout = 0;
630 Char *ft = cp, *dp, *ep, *strdev, *strino, *strF, *str, valtest = '\0',
631 *errval = STR0;
632 char *string, string0[22 + MB_LEN_MAX + 1]; /* space for 64 bit octal */
633 time_t footime;
634 struct passwd *pw;
635 struct group *gr;
636
637 while (any(FILETESTS, *++ft))
638 continue;
639
640 if (!*ft && *(ft - 1) == 'L')
641 --ft;
642
643 if (any(FILEVALS, *ft)) {
644 valtest = *ft++;
645 /*
646 * Value tests return '-1' on failure as 0 is
647 * a legitimate value for many of them.
648 * 'F' returns ':' for compatibility.
649 */
650 errval = valtest == 'F' ? STRcolon : STRminus1;
651
652 if (valtest == 'P' && *ft >= '0' && *ft <= '7') {
653 pmask = (char) *ft - '0';
654 while ( *++ft >= '0' && *ft <= '7' )
655 pmask = 8 * pmask + ((char) *ft - '0');
656 }
657 if (Strcmp(ft, STRcolon) == 0 && any("AMCUGP", valtest)) {
658 altout = 1;
659 ++ft;
660 }
661 }
662
663 if (*ft || ft == cp + 1)
664 stderror(ERR_NAME | ERR_FILEINQ);
665
666 /*
667 * Detect missing file names by checking for operator in the file name
668 * position. However, if an operator name appears there, we must make
669 * sure that there's no file by that name (e.g., "/") before announcing
670 * an error. Even this check isn't quite right, since it doesn't take
671 * globbing into account.
672 */
673
674 if (isa(**vp, ANYOP) && TCSH_STAT(short2str(**vp), &stb))
675 stderror(ERR_NAME | ERR_FILENAME);
676
677 dp = *(*vp)++;
678 if (ignore & TEXP_IGNORE)
679 return (Strsave(STRNULL));
680 ep = globone(dp, G_APPEND);
681 cleanup_push(ep, xfree);
682 ft = &cp[1];
683 do
684 switch (*ft) {
685
686 case 'r':
687 i = !sh_access(ep, R_OK);
688 break;
689
690 case 'w':
691 i = !sh_access(ep, W_OK);
692 break;
693
694 case 'x':
695 i = !sh_access(ep, X_OK);
696 break;
697
698 case 'X': /* tcsh extension, name is an executable in the path
699 * or a tcsh builtin command
700 */
701 i = find_cmd(ep, 0);
702 break;
703
704 case 't': /* SGI extension, true when file is a tty */
705 i = isatty(atoi(short2str(ep)));
706 break;
707
708 default:
709
710 #ifdef S_IFLNK
711 if (tolower(*ft) == 'l') {
712 /*
713 * avoid convex compiler bug.
714 */
715 if (!lst) {
716 lst = &lstb;
717 if (TCSH_LSTAT(short2str(ep), lst) == -1) {
718 cleanup_until(ep);
719 return (Strsave(errval));
720 }
721 }
722 if (*ft == 'L')
723 st = lst;
724 }
725 else
726 #endif /* S_IFLNK */
727 /*
728 * avoid convex compiler bug.
729 */
730 if (!st) {
731 st = &stb;
732 if (TCSH_STAT(short2str(ep), st) == -1) {
733 cleanup_until(ep);
734 return (Strsave(errval));
735 }
736 }
737
738 switch (*ft) {
739
740 case 'f':
741 #ifdef S_ISREG
742 i = S_ISREG(st->st_mode);
743 #else /* !S_ISREG */
744 i = 0;
745 #endif /* S_ISREG */
746 break;
747
748 case 'd':
749 #ifdef S_ISDIR
750 i = S_ISDIR(st->st_mode);
751 #else /* !S_ISDIR */
752 i = 0;
753 #endif /* S_ISDIR */
754 break;
755
756 case 'p':
757 #ifdef S_ISFIFO
758 i = S_ISFIFO(st->st_mode);
759 #else /* !S_ISFIFO */
760 i = 0;
761 #endif /* S_ISFIFO */
762 break;
763
764 case 'm' :
765 #ifdef S_ISOFL
766 i = S_ISOFL(st->st_dm_mode);
767 #else /* !S_ISOFL */
768 i = 0;
769 #endif /* S_ISOFL */
770 break ;
771
772 case 'K' :
773 #ifdef S_ISOFL
774 i = stb.st_dm_key;
775 #else /* !S_ISOFL */
776 i = 0;
777 #endif /* S_ISOFL */
778 break ;
779
780
781 case 'l':
782 #ifdef S_ISLNK
783 i = S_ISLNK(lst->st_mode);
784 #else /* !S_ISLNK */
785 i = 0;
786 #endif /* S_ISLNK */
787 break;
788
789 case 'S':
790 # ifdef S_ISSOCK
791 i = S_ISSOCK(st->st_mode);
792 # else /* !S_ISSOCK */
793 i = 0;
794 # endif /* S_ISSOCK */
795 break;
796
797 case 'b':
798 #ifdef S_ISBLK
799 i = S_ISBLK(st->st_mode);
800 #else /* !S_ISBLK */
801 i = 0;
802 #endif /* S_ISBLK */
803 break;
804
805 case 'c':
806 #ifdef S_ISCHR
807 i = S_ISCHR(st->st_mode);
808 #else /* !S_ISCHR */
809 i = 0;
810 #endif /* S_ISCHR */
811 break;
812
813 case 'u':
814 i = (S_ISUID & st->st_mode) != 0;
815 break;
816
817 case 'g':
818 i = (S_ISGID & st->st_mode) != 0;
819 break;
820
821 case 'k':
822 i = (S_ISVTX & st->st_mode) != 0;
823 break;
824
825 case 'z':
826 i = st->st_size == 0;
827 break;
828
829 #ifdef convex
830 case 'R':
831 i = (stb.st_dmonflags & IMIGRATED) == IMIGRATED;
832 break;
833 #endif /* convex */
834
835 case 's':
836 i = stb.st_size != 0;
837 break;
838
839 case 'e':
840 i = 1;
841 break;
842
843 case 'o':
844 i = st->st_uid == uid;
845 break;
846
847 /*
848 * Value operators are a tcsh extension.
849 */
850
851 case 'D':
852 i = (tcsh_number_t) st->st_dev;
853 break;
854
855 case 'I':
856 i = (tcsh_number_t) st->st_ino;
857 break;
858
859 case 'F':
860 strdev = putn( (int) st->st_dev);
861 strino = putn( (int) st->st_ino);
862 strF = xmalloc((2 + Strlen(strdev) + Strlen(strino))
863 * sizeof(Char));
864 (void) Strcat(Strcat(Strcpy(strF, strdev), STRcolon), strino);
865 xfree(strdev);
866 xfree(strino);
867 cleanup_until(ep);
868 return(strF);
869
870 case 'L':
871 if ( *(ft + 1) ) {
872 i = 1;
873 break;
874 }
875 #ifdef S_ISLNK
876 filnam = short2str(ep);
877 string = areadlink(filnam);
878 strF = string == NULL ? errval : str2short(string);
879 xfree(string);
880 cleanup_until(ep);
881 return(Strsave(strF));
882
883 #else /* !S_ISLNK */
884 i = 0;
885 break;
886 #endif /* S_ISLNK */
887
888
889 case 'N':
890 i = (tcsh_number_t) st->st_nlink;
891 break;
892
893 case 'P':
894 string = string0 + 1;
895 (void) xsnprintf(string, sizeof(string0) - 1, "%o",
896 pmask & (unsigned int)
897 ((S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID) & st->st_mode));
898 if (altout && *string != '0')
899 *--string = '0';
900 cleanup_until(ep);
901 return(Strsave(str2short(string)));
902
903 case 'U':
904 if (altout && (pw = xgetpwuid(st->st_uid))) {
905 cleanup_until(ep);
906 return(Strsave(str2short(pw->pw_name)));
907 }
908 i = (tcsh_number_t) st->st_uid;
909 break;
910
911 case 'G':
912 if (altout && (gr = xgetgrgid(st->st_gid))) {
913 cleanup_until(ep);
914 return(Strsave(str2short(gr->gr_name)));
915 }
916 i = (tcsh_number_t) st->st_gid;
917 break;
918
919 case 'Z':
920 i = (tcsh_number_t) st->st_size;
921 break;
922
923 case 'A': case 'M': case 'C':
924 footime = *ft == 'A' ? st->st_atime :
925 *ft == 'M' ? st->st_mtime : st->st_ctime;
926 if (altout) {
927 strF = str2short(ctime(&footime));
928 if ((str = Strchr(strF, '\n')) != NULL)
929 *str = (Char) '\0';
930 cleanup_until(ep);
931 return(Strsave(strF));
932 }
933 i = (tcsh_number_t) footime;
934 break;
935
936 }
937 }
938 while (*++ft && i);
939 etraci("exp6 -? i", i, vp);
940 cleanup_until(ep);
941 return (putn(i));
942 }
943
944
945 static void
evalav(Char ** v)946 evalav(Char **v)
947 {
948 struct wordent paraml1;
949 struct wordent *hp = ¶ml1;
950 struct command *t;
951 struct wordent *wdp = hp;
952
953 setcopy(STRstatus, STR0, VAR_READWRITE);
954 initlex(hp);
955 while (*v) {
956 struct wordent *new = xcalloc(1, sizeof *wdp);
957
958 new->prev = wdp;
959 new->next = hp;
960 wdp->next = new;
961 wdp = new;
962 wdp->word = Strsave(*v++);
963 }
964 hp->prev = wdp;
965 cleanup_push(¶ml1, lex_cleanup);
966 alias(¶ml1);
967 t = syntax(paraml1.next, ¶ml1, 0);
968 cleanup_push(t, syntax_cleanup);
969 if (seterr)
970 stderror(ERR_OLD);
971 execute(t, -1, NULL, NULL, TRUE);
972 cleanup_until(¶ml1);
973 }
974
975 static int
isa(Char * cp,int what)976 isa(Char *cp, int what)
977 {
978 if (cp == 0)
979 return ((what & RESTOP) != 0);
980 if (*cp == '\0')
981 return 0;
982 if (cp[1] == 0) {
983 if (what & ADDOP && (*cp == '+' || *cp == '-'))
984 return (1);
985 if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%'))
986 return (1);
987 if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' ||
988 *cp == '~' || *cp == '^' || *cp == '"'))
989 return (1);
990 }
991 else if (cp[2] == 0) {
992 if (what & RESTOP) {
993 if (cp[0] == '|' && cp[1] == '&')
994 return (1);
995 if (cp[0] == '<' && cp[1] == '<')
996 return (1);
997 if (cp[0] == '>' && cp[1] == '>')
998 return (1);
999 }
1000 if (what & EQOP) {
1001 if (cp[0] == '=') {
1002 if (cp[1] == '=')
1003 return (EQEQ);
1004 if (cp[1] == '~')
1005 return (EQMATCH);
1006 }
1007 else if (cp[0] == '!') {
1008 if (cp[1] == '=')
1009 return (NOTEQ);
1010 if (cp[1] == '~')
1011 return (NOTEQMATCH);
1012 }
1013 }
1014 }
1015 if (what & RELOP) {
1016 if (*cp == '<')
1017 return (LSS);
1018 if (*cp == '>')
1019 return (GTR);
1020 }
1021 return (0);
1022 }
1023
1024 static tcsh_number_t
egetn(const Char * cp)1025 egetn(const Char *cp)
1026 {
1027 if (*cp && *cp != '-' && !Isdigit(*cp))
1028 stderror(ERR_NAME | ERR_EXPRESSION);
1029 return (getn(cp));
1030 }
1031
1032 /* Phew! */
1033
1034 #ifdef EDEBUG
1035 static void
etraci(const char * str,tcsh_number_t i,Char *** vp)1036 etraci(const char *str, tcsh_number_t i, Char ***vp)
1037 {
1038 #ifdef HAVE_LONG_LONG
1039 xprintf("%s=%lld\t", str, i);
1040 #else
1041 xprintf("%s=%ld\t", str, i);
1042 #endif
1043 blkpr(*vp);
1044 xputchar('\n');
1045 }
1046 static void
etracc(const char * str,const Char * cp,Char *** vp)1047 etracc(const char *str, const Char *cp, Char ***vp)
1048 {
1049 xprintf("%s=%S\t", str, cp);
1050 blkpr(*vp);
1051 xputchar('\n');
1052 }
1053 #endif /* EDEBUG */
1054