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