sh.exp.c 21.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
/*
 * sh.exp.c: Expression evaluations
 */
/*-
 * Copyright (c) 1980, 1991 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "sh.h"
#include "tw.h"

/*
 * C shell
 */

#define TEXP_IGNORE 1	/* in ignore, it means to ignore value, just parse */
#define TEXP_NOGLOB 2	/* in ignore, it means not to globone */

#define	ADDOP	1
#define	MULOP	2
#define	EQOP	4
#define	RELOP	8
#define	RESTOP	16
#define	ANYOP	31

#define	EQEQ	1
#define	GTR	2
#define	LSS	4
#define	NOTEQ	6
#define EQMATCH 7
#define NOTEQMATCH 8

56 57 58 59 60 61 62 63 64 65 66 67 68 69
static	int	   sh_access	(const Char *, int);
static	tcsh_number_t  exp1		(Char ***, int);
static	tcsh_number_t  exp2x	(Char ***, int);
static	tcsh_number_t  exp2a	(Char ***, int);
static	tcsh_number_t  exp2b	(Char ***, int);
static	tcsh_number_t  exp2c	(Char ***, int);
static	Char 	  *exp3		(Char ***, int);
static	Char 	  *exp3a	(Char ***, int);
static	Char 	  *exp4		(Char ***, int);
static	Char 	  *exp5		(Char ***, int);
static	Char 	  *exp6		(Char ***, int);
static	void	   evalav	(Char **);
static	int	   isa		(Char *, int);
static	tcsh_number_t  egetn	(const Char *);
70 71

#ifdef EDEBUG
72 73
static	void	   etracc	(const char *, const Char *, Char ***);
static	void	   etraci	(const char *, tcsh_number_t, Char ***);
74 75 76 77
#else /* !EDEBUG */
#define etracc(A, B, C) ((void)0)
#define etraci(A, B, C) ((void)0)
#endif /* !EDEBUG */
78 79 80 81 82 83

/*
 * shell access function according to POSIX and non POSIX
 * From Beto Appleton (beto@aixwiz.aix.ibm.com)
 */
static int
84
sh_access(const Char *fname, int mode)
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
{
#if defined(POSIX) && !defined(USE_ACCESS)
    struct stat     statb;
#endif /* POSIX */
    char *name = short2str(fname);

    if (*name == '\0')
	return 1;

#if !defined(POSIX) || defined(USE_ACCESS)
    return access(name, mode);
#else /* POSIX */

    /*
     * POSIX 1003.2-d11.2 
     *	-r file		True if file exists and is readable. 
     *	-w file		True if file exists and is writable. 
     *			True shall indicate only that the write flag is on. 
     *			The file shall not be writable on a read-only file
     *			system even if this test indicates true.
     *	-x file		True if file exists and is executable. 
     *			True shall indicate only that the execute flag is on. 
     *			If file is a directory, true indicates that the file 
     *			can be searched.
     */
    if (mode != W_OK && mode != X_OK)
	return access(name, mode);

    if (stat(name, &statb) == -1) 
	return 1;

    if (access(name, mode) == 0) {
#ifdef S_ISDIR
	if (S_ISDIR(statb.st_mode) && mode == X_OK)
	    return 0;
#endif /* S_ISDIR */

	/* root needs permission for someone */
	switch (mode) {
	case W_OK:
	    mode = S_IWUSR | S_IWGRP | S_IWOTH;
	    break;
	case X_OK:
	    mode = S_IXUSR | S_IXGRP | S_IXOTH;
	    break;
	default:
	    abort();
	    break;
	}

    } 

    else if (euid == statb.st_uid)
	mode <<= 6;

    else if (egid == statb.st_gid)
	mode <<= 3;

# ifdef NGROUPS_MAX
    else {
	/* you can be in several groups */
	long	n;
	GETGROUPS_T *groups;

	/*
	 * Try these things to find a positive maximum groups value:
	 *   1) sysconf(_SC_NGROUPS_MAX)
	 *   2) NGROUPS_MAX
	 *   3) getgroups(0, unused)
	 * Then allocate and scan the groups array if one of these worked.
	 */
#  if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX)
	if ((n = sysconf(_SC_NGROUPS_MAX)) == -1)
#  endif /* _SC_NGROUPS_MAX */
	    n = NGROUPS_MAX;
	if (n <= 0)
	    n = getgroups(0, (GETGROUPS_T *) NULL);

	if (n > 0) {
164
	    groups = xmalloc(n * sizeof(*groups));
165 166 167 168 169 170
	    n = getgroups((int) n, groups);
	    while (--n >= 0)
		if (groups[n] == statb.st_gid) {
		    mode <<= 3;
		    break;
		}
171
	    xfree(groups);
172 173 174 175 176 177 178 179 180 181 182
	}
    }
# endif /* NGROUPS_MAX */

    if (statb.st_mode & mode)
	return 0;
    else
	return 1;
#endif /* !POSIX */
}

183
tcsh_number_t
184
expr(Char ***vp)
185 186 187 188
{
    return (exp0(vp, 0));
}

189
tcsh_number_t
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 197 198
	int p2;

	(*vp)++;
199 200 201 202 203 204 205 206 207

	p2 = compat_expr ? 
	    exp0(vp, (ignore & TEXP_IGNORE) || p1) :
	    exp1(vp, (ignore & TEXP_IGNORE) || p1);
	if (compat_expr || !(ignore & TEXP_IGNORE))
	    p1 = (p1 || p2);
	etraci("exp0 p1", p1, vp);
	if (compat_expr)
	    break;
208 209 210 211
    }
    return (p1);
}

212
static tcsh_number_t
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 223 224 225
	p2 = compat_expr ?
	    exp1(vp, (ignore & TEXP_IGNORE) || !p1) :
	    exp2x(vp, (ignore & TEXP_IGNORE) || !p1);

226
	etraci("exp1 p2", p2, vp);
227 228 229 230 231
	if (compat_expr || !(ignore & TEXP_IGNORE))
	    p1 = (p1 && p2);
	etraci("exp1 p1", p1, vp);
	if (compat_expr)
	    break;
232 233 234 235
    }
    return (p1);
}

236
static tcsh_number_t
237
exp2x(Char ***vp, int ignore)
238
{
239
    tcsh_number_t p1 = exp2a(vp, ignore);
240

241 242
    etraci("exp2x p1", p1, vp);
    while (**vp && eq(**vp, STRor)) {
243
	tcsh_number_t p2;
244 245

	(*vp)++;
246 247 248 249 250 251 252 253 254
	p2 = compat_expr ?
	    exp2x(vp, ignore) :
	    exp2a(vp, ignore);
	etraci("exp2x p2", p2, vp);
	if (compat_expr || !(ignore & TEXP_IGNORE))
		p1 = (p1 | p2);
	etraci("exp2x p1", p1, vp);
	if (compat_expr)
	    break;
255 256 257 258
    }
    return (p1);
}

259
static tcsh_number_t
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 270 271
	p2 = compat_expr ?
	    exp2a(vp, ignore) :
	    exp2b(vp, ignore);
272
	etraci("exp2a p2", p2, vp);
273 274 275 276 277
	if (compat_expr || !(ignore & TEXP_IGNORE))
	    p1 = (p1 ^ p2);
	etraci("exp2a p1", p1, vp);
	if (compat_expr)
	    break;
278 279 280 281
    }
    return (p1);
}

282
static tcsh_number_t
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 293 294
	p2 = compat_expr ?
	    exp2b(vp, ignore) :
	    exp2c(vp, ignore);
295
	etraci("exp2b p2", p2, vp);
296 297 298 299 300
	if (compat_expr || !(ignore & TEXP_IGNORE))
	    p1 = (p1 & p2);
	etraci("exp2b p1", p1, vp);
	if (compat_expr)
	    break;
301 302 303 304
    }
    return (p1);
}

305
static tcsh_number_t
306
exp2c(Char ***vp, int ignore)
307 308 309
{
    Char *p1 = exp3(vp, ignore);
    Char *p2;
310
    tcsh_number_t i;
311

312
    cleanup_push(p1, xfree);
313 314 315 316 317 318
    etracc("exp2c p1", p1, vp);
    if ((i = isa(**vp, EQOP)) != 0) {
	(*vp)++;
	if (i == EQMATCH || i == NOTEQMATCH)
	    ignore |= TEXP_NOGLOB;
	p2 = exp3(vp, ignore);
319
	cleanup_push(p2, xfree);
320 321
	etracc("exp2c p2", p2, vp);
	if (!(ignore & TEXP_IGNORE))
322
	    switch ((int)i) {
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339

	    case EQEQ:
		i = eq(p1, p2);
		break;

	    case NOTEQ:
		i = !eq(p1, p2);
		break;

	    case EQMATCH:
		i = Gmatch(p1, p2);
		break;

	    case NOTEQMATCH:
		i = !Gmatch(p1, p2);
		break;
	    }
340
	cleanup_until(p1);
341 342 343
	return (i);
    }
    i = egetn(p1);
344
    cleanup_until(p1);
345 346 347 348
    return (i);
}

static Char *
349
exp3(Char ***vp, int ignore)
350 351
{
    Char *p1, *p2;
352
    tcsh_number_t i;
353 354 355

    p1 = exp3a(vp, ignore);
    etracc("exp3 p1", p1, vp);
356
    while ((i = isa(**vp, RELOP)) != 0) {
357 358 359
	(*vp)++;
	if (**vp && eq(**vp, STRequal))
	    i |= 1, (*vp)++;
360
	cleanup_push(p1, xfree);
361 362 363
	p2 = compat_expr ?
	    exp3(vp, ignore) :
	    exp3a(vp, ignore);
364
	cleanup_push(p2, xfree);
365 366
	etracc("exp3 p2", p2, vp);
	if (!(ignore & TEXP_IGNORE))
367
	    switch ((int)i) {
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384

	    case GTR:
		i = egetn(p1) > egetn(p2);
		break;

	    case GTR | 1:
		i = egetn(p1) >= egetn(p2);
		break;

	    case LSS:
		i = egetn(p1) < egetn(p2);
		break;

	    case LSS | 1:
		i = egetn(p1) <= egetn(p2);
		break;
	    }
385
	cleanup_until(p1);
386 387 388 389
	p1 = putn(i);
	etracc("exp3 p1", p1, vp);
	if (compat_expr)
	    break;
390 391 392 393 394
    }
    return (p1);
}

static Char *
395
exp3a(Char ***vp, int ignore)
396
{
397 398
    Char *p1, *p2;
    const Char *op;
399
    tcsh_number_t i;
400 401 402 403 404 405

    p1 = exp4(vp, ignore);
    etracc("exp3a p1", p1, vp);
    op = **vp;
    if (op && any("<>", op[0]) && op[0] == op[1]) {
	(*vp)++;
406
	cleanup_push(p1, xfree);
407 408 409
	p2 = compat_expr ?
	    exp3a(vp, ignore) :
	    exp4(vp, ignore);
410
	cleanup_push(p2, xfree);
411 412 413 414 415
	etracc("exp3a p2", p2, vp);
	if (op[0] == '<')
	    i = egetn(p1) << egetn(p2);
	else
	    i = egetn(p1) >> egetn(p2);
416
	cleanup_until(p1);
417 418
	p1 = putn(i);
	etracc("exp3a p1", p1, vp);
419 420 421 422 423
    }
    return (p1);
}

static Char *
424
exp4(Char ***vp, int ignore)
425 426
{
    Char *p1, *p2;
427
    tcsh_number_t i = 0;
428 429 430

    p1 = exp5(vp, ignore);
    etracc("exp4 p1", p1, vp);
431
    while (isa(**vp, ADDOP)) {
432
	const Char *op = *(*vp)++;
433

434
	cleanup_push(p1, xfree);
435 436 437
	p2 = compat_expr ?
	    exp4(vp, ignore) :
	    exp5(vp, ignore);
438
	cleanup_push(p2, xfree);
439 440 441 442 443 444 445 446 447 448 449 450
	etracc("exp4 p2", p2, vp);
	if (!(ignore & TEXP_IGNORE))
	    switch (op[0]) {

	    case '+':
		i = egetn(p1) + egetn(p2);
		break;

	    case '-':
		i = egetn(p1) - egetn(p2);
		break;
	    }
451
	cleanup_until(p1);
452 453 454 455
	p1 = putn(i);
	etracc("exp4 p1", p1, vp);
	if (compat_expr)
	    break;
456 457 458 459 460
    }
    return (p1);
}

static Char *
461
exp5(Char ***vp, int ignore)
462 463
{
    Char *p1, *p2;
464
    tcsh_number_t i = 0;
465 466 467 468

    p1 = exp6(vp, ignore);
    etracc("exp5 p1", p1, vp);

469
    while (isa(**vp, MULOP)) {
470 471 472
	const Char *op = *(*vp)++;
	if ((ignore & TEXP_NOGLOB) != 0) {
	    /*
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 482 483
	p2 = compat_expr ? 
	    exp5(vp, ignore) :
	    exp6(vp, ignore);
484
	cleanup_push(p2, xfree);
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
	etracc("exp5 p2", p2, vp);
	if (!(ignore & TEXP_IGNORE))
	    switch (op[0]) {

	    case '*':
		i = egetn(p1) * egetn(p2);
		break;

	    case '/':
		i = egetn(p2);
		if (i == 0)
		    stderror(ERR_DIV0);
		i = egetn(p1) / i;
		break;

	    case '%':
		i = egetn(p2);
		if (i == 0)
		    stderror(ERR_MOD0);
		i = egetn(p1) % i;
		break;
	    }
507
	cleanup_until(p1);
508 509 510 511
	p1 = putn(i);
	etracc("exp5 p1", p1, vp);
	if (compat_expr)
	    break;
512 513 514 515 516
    }
    return (p1);
}

static Char *
517
exp6(Char ***vp, int ignore)
518
{
519 520
    tcsh_number_t ccode;
    tcsh_number_t i = 0;
521 522 523 524 525 526 527
    Char *cp;

    if (**vp == 0)
	stderror(ERR_NAME | ERR_EXPRESSION);
    if (eq(**vp, STRbang)) {
	(*vp)++;
	cp = exp6(vp, ignore);
528
	cleanup_push(cp, xfree);
529 530
	etracc("exp6 ! cp", cp, vp);
	i = egetn(cp);
531
	cleanup_until(cp);
532 533 534 535 536
	return (putn(!i));
    }
    if (eq(**vp, STRtilde)) {
	(*vp)++;
	cp = exp6(vp, ignore);
537
	cleanup_push(cp, xfree);
538 539
	etracc("exp6 ~ cp", cp, vp);
	i = egetn(cp);
540
	cleanup_until(cp);
541 542 543 544 545 546
	return (putn(~i));
    }
    if (eq(**vp, STRLparen)) {
	(*vp)++;
	ccode = exp0(vp, ignore);
	etraci("exp6 () ccode", ccode, vp);
547
	if (**vp == 0 || ***vp != ')')
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
	    stderror(ERR_NAME | ERR_EXPRESSION);
	(*vp)++;
	return (putn(ccode));
    }
    if (eq(**vp, STRLbrace)) {
	Char **v;
	struct command faket;
	Char   *fakecom[2];

	faket.t_dtyp = NODE_COMMAND;
	faket.t_dflg = F_BACKQ;
	faket.t_dcar = faket.t_dcdr = faket.t_dspr = NULL;
	faket.t_dcom = fakecom;
	fakecom[0] = STRfakecom;
	fakecom[1] = NULL;
	(*vp)++;
	v = *vp;
	for (;;) {
	    if (!**vp)
		stderror(ERR_NAME | ERR_MISSING, '}');
	    if (eq(*(*vp)++, STRRbrace))
		break;
	}
	if (ignore & TEXP_IGNORE)
	    return (Strsave(STRNULL));
	psavejob();
574
	cleanup_push(&faket, psavejob_cleanup); /* faket is only a marker */
575 576 577 578 579 580
	if (pfork(&faket, -1) == 0) {
	    *--(*vp) = 0;
	    evalav(v);
	    exitstat();
	}
	pwait();
581
	cleanup_until(&faket);
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
	etraci("exp6 {} status", egetn(varval(STRstatus)), vp);
	return (putn(egetn(varval(STRstatus)) == 0));
    }
    if (isa(**vp, ANYOP))
	return (Strsave(STRNULL));
    cp = *(*vp)++;
#ifdef convex
# define FILETESTS "erwxfdzoplstSXLbcugkmKR"
#else
# define FILETESTS "erwxfdzoplstSXLbcugkmK"
#endif /* convex */
#define FILEVALS  "ZAMCDIUGNFPL"
    if (*cp == '-' && (any(FILETESTS, cp[1]) || any(FILEVALS, cp[1])))
        return(filetest(cp, vp, ignore));
    etracc("exp6 default", cp, vp);
    return (ignore & TEXP_NOGLOB ? Strsave(cp) : globone(cp, G_APPEND));
}


/* 
 * Extended file tests
 * From: John Rowe <rowe@excc.exeter.ac.uk>
 */
Char *
606
filetest(Char *cp, Char ***vp, int ignore)
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
{
#ifdef convex
    struct cvxstat stb, *st = NULL;
# define TCSH_STAT	stat64
#else
# define TCSH_STAT	stat
    struct stat stb, *st = NULL;
#endif /* convex */

#ifdef S_IFLNK
# ifdef convex
    struct cvxstat lstb, *lst = NULL;
#  define TCSH_LSTAT lstat64
# else
#  define TCSH_LSTAT lstat
    struct stat lstb, *lst = NULL;
# endif /* convex */
    char *filnam;
#endif /* S_IFLNK */

627
    tcsh_number_t i = 0;
628 629 630 631
    unsigned pmask = 0xffff;
    int altout = 0;
    Char *ft = cp, *dp, *ep, *strdev, *strino, *strF, *str, valtest = '\0',
    *errval = STR0;
632
    char *string, string0[22 + MB_LEN_MAX + 1];	/* space for 64 bit octal */
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
    time_t footime;
    struct passwd *pw;
    struct group *gr;

    while(any(FILETESTS, *++ft))
	continue;

    if (!*ft && *(ft - 1) == 'L')
	--ft;

    if (any(FILEVALS, *ft)) {
	valtest = *ft++;
	/*
	 * Value tests return '-1' on failure as 0 is
	 * a legitimate value for many of them.
	 * 'F' returns ':' for compatibility.
	 */
	errval = valtest == 'F' ? STRcolon : STRminus1;

	if (valtest == 'P' && *ft >= '0' && *ft <= '7') {
	    pmask = (char) *ft - '0';
	    while ( *++ft >= '0' && *ft <= '7' )
		pmask = 8 * pmask + ((char) *ft - '0');
	}
	if (Strcmp(ft, STRcolon) == 0 && any("AMCUGP", valtest)) {
	    altout = 1;
	    ++ft;
	}
    }

    if (*ft || ft == cp + 1)
	stderror(ERR_NAME | ERR_FILEINQ);

    /*
     * Detect missing file names by checking for operator in the file name
     * position.  However, if an operator name appears there, we must make
     * sure that there's no file by that name (e.g., "/") before announcing
     * an error.  Even this check isn't quite right, since it doesn't take
     * globbing into account.
     */

    if (isa(**vp, ANYOP) && TCSH_STAT(short2str(**vp), &stb))
	stderror(ERR_NAME | ERR_FILENAME);

    dp = *(*vp)++;
    if (ignore & TEXP_IGNORE)
	return (Strsave(STRNULL));
    ep = globone(dp, G_APPEND);
681
    cleanup_push(ep, xfree);
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
    ft = &cp[1];
    do 
	switch (*ft) {

	case 'r':
	    i = !sh_access(ep, R_OK);
	    break;

	case 'w':
	    i = !sh_access(ep, W_OK);
	    break;

	case 'x':
	    i = !sh_access(ep, X_OK);
	    break;

	case 'X':	/* tcsh extension, name is an executable in the path
			 * or a tcsh builtin command 
			 */
	    i = find_cmd(ep, 0);
	    break;

	case 't':	/* SGI extension, true when file is a tty */
	    i = isatty(atoi(short2str(ep)));
	    break;

	default:

#ifdef S_IFLNK
	    if (tolower(*ft) == 'l') {
		/* 
		 * avoid convex compiler bug.
		 */
		if (!lst) {
		    lst = &lstb;
		    if (TCSH_LSTAT(short2str(ep), lst) == -1) {
718
			cleanup_until(ep);
719 720 721 722 723 724 725 726 727 728 729 730 731 732
			return (Strsave(errval));
		    }
		}
		if (*ft == 'L')
		    st = lst;
	    }
	    else 
#endif /* S_IFLNK */
		/* 
		 * avoid convex compiler bug.
		 */
		if (!st) {
		    st = &stb;
		    if (TCSH_STAT(short2str(ep), st) == -1) {
733
			cleanup_until(ep);
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
			return (Strsave(errval));
		    }
		}

	    switch (*ft) {

	    case 'f':
#ifdef S_ISREG
		i = S_ISREG(st->st_mode);
#else /* !S_ISREG */
		i = 0;
#endif /* S_ISREG */
		break;

	    case 'd':
#ifdef S_ISDIR
		i = S_ISDIR(st->st_mode);
#else /* !S_ISDIR */
		i = 0;
#endif /* S_ISDIR */
		break;

	    case 'p':
#ifdef S_ISFIFO
		i = S_ISFIFO(st->st_mode);
#else /* !S_ISFIFO */
		i = 0;
#endif /* S_ISFIFO */
		break;

	    case 'm' :
#ifdef S_ISOFL
	      i = S_ISOFL(st->st_dm_mode);
#else /* !S_ISOFL */
	      i = 0;
#endif /* S_ISOFL */
	      break ;

	    case 'K' :
#ifdef S_ISOFL
	      i = stb.st_dm_key;
#else /* !S_ISOFL */
	      i = 0;
#endif /* S_ISOFL */
	      break ;
  

	    case 'l':
#ifdef S_ISLNK
		i = S_ISLNK(lst->st_mode);
#else /* !S_ISLNK */
		i = 0;
#endif /* S_ISLNK */
		break;

	    case 'S':
# ifdef S_ISSOCK
		i = S_ISSOCK(st->st_mode);
# else /* !S_ISSOCK */
		i = 0;
# endif /* S_ISSOCK */
		break;

	    case 'b':
#ifdef S_ISBLK
		i = S_ISBLK(st->st_mode);
#else /* !S_ISBLK */
		i = 0;
#endif /* S_ISBLK */
		break;

	    case 'c':
#ifdef S_ISCHR
		i = S_ISCHR(st->st_mode);
#else /* !S_ISCHR */
		i = 0;
#endif /* S_ISCHR */
		break;

	    case 'u':
		i = (S_ISUID & st->st_mode) != 0;
		break;

	    case 'g':
		i = (S_ISGID & st->st_mode) != 0;
		break;

	    case 'k':
		i = (S_ISVTX & st->st_mode) != 0;
		break;

	    case 'z':
		i = st->st_size == 0;
		break;

#ifdef convex
	    case 'R':
		i = (stb.st_dmonflags & IMIGRATED) == IMIGRATED;
		break;
#endif /* convex */

	    case 's':
		i = stb.st_size != 0;
		break;

	    case 'e':
		i = 1;
		break;

	    case 'o':
		i = st->st_uid == uid;
		break;

		/*
		 * Value operators are a tcsh extension.
		 */

	    case 'D':
852
		i = (tcsh_number_t) st->st_dev;
853 854 855
		break;

	    case 'I':
856
		i = (tcsh_number_t) st->st_ino;
857 858 859 860 861
		break;
		
	    case 'F':
		strdev = putn( (int) st->st_dev);
		strino = putn( (int) st->st_ino);
862 863
		strF = xmalloc((2 + Strlen(strdev) + Strlen(strino))
			       * sizeof(Char));
864
		(void) Strcat(Strcat(Strcpy(strF, strdev), STRcolon), strino);
865 866 867
		xfree(strdev);
		xfree(strino);
		cleanup_until(ep);
868 869 870 871 872 873 874 875 876
		return(strF);
		
	    case 'L':
		if ( *(ft + 1) ) {
		    i = 1;
		    break;
		}
#ifdef S_ISLNK
		filnam = short2str(ep);
877 878 879 880
		string = areadlink(filnam);
		strF = string == NULL ? errval : str2short(string);
		xfree(string);
		cleanup_until(ep);
881 882 883 884 885 886 887 888 889
		return(Strsave(strF));

#else /* !S_ISLNK */
		i = 0;
		break;
#endif /* S_ISLNK */
		

	    case 'N':
890
		i = (tcsh_number_t) st->st_nlink;
891 892 893 894 895 896 897 898 899
		break;

	    case 'P':
		string = string0 + 1;
		(void) xsnprintf(string, sizeof(string0) - 1, "%o",
		    pmask & (unsigned int) 
		    ((S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID) & st->st_mode));
		if (altout && *string != '0')
		    *--string = '0';
900
		cleanup_until(ep);
901 902 903
		return(Strsave(str2short(string)));

	    case 'U':
904 905
		if (altout && (pw = xgetpwuid(st->st_uid))) {
		    cleanup_until(ep);
906 907
		    return(Strsave(str2short(pw->pw_name)));
		}
908
		i = (tcsh_number_t) st->st_uid;
909 910 911
		break;

	    case 'G':
912 913
		if (altout && (gr = xgetgrgid(st->st_gid))) {
		    cleanup_until(ep);
914 915
		    return(Strsave(str2short(gr->gr_name)));
		}
916
		i = (tcsh_number_t) st->st_gid;
917 918 919
		break;

	    case 'Z':
920
		i = (tcsh_number_t) st->st_size;
921 922 923 924 925 926 927 928 929
		break;

	    case 'A': case 'M': case 'C':
		footime = *ft == 'A' ? st->st_atime :
		    *ft == 'M' ? st->st_mtime : st->st_ctime;
		if (altout) {
		    strF = str2short(ctime(&footime));
		    if ((str = Strchr(strF, '\n')) != NULL)
			*str = (Char) '\0';
930
		    cleanup_until(ep);
931 932
		    return(Strsave(strF));
		}
933
		i = (tcsh_number_t) footime;
934 935 936 937 938 939
		break;

	    }
	}
    while (*++ft && i);
    etraci("exp6 -? i", i, vp);
940
    cleanup_until(ep);
941 942 943 944 945
    return (putn(i));
}


static void
946
evalav(Char **v)
947 948 949 950 951 952
{
    struct wordent paraml1;
    struct wordent *hp = &paraml1;
    struct command *t;
    struct wordent *wdp = hp;

953
    setcopy(STRstatus, STR0, VAR_READWRITE);
954
    initlex(hp);
955
    while (*v) {
956
	struct wordent *new = xcalloc(1, sizeof *wdp);
957 958 959 960 961 962 963 964

	new->prev = wdp;
	new->next = hp;
	wdp->next = new;
	wdp = new;
	wdp->word = Strsave(*v++);
    }
    hp->prev = wdp;
965
    cleanup_push(&paraml1, lex_cleanup);
966 967
    alias(&paraml1);
    t = syntax(paraml1.next, &paraml1, 0);
968
    cleanup_push(t, syntax_cleanup);
969 970 971
    if (seterr)
	stderror(ERR_OLD);
    execute(t, -1, NULL, NULL, TRUE);
972
    cleanup_until(&paraml1);
973 974 975
}

static int
976
isa(Char *cp, int what)
977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
{
    if (cp == 0)
	return ((what & RESTOP) != 0);
    if (*cp == '\0')
    	return 0;
    if (cp[1] == 0) {
	if (what & ADDOP && (*cp == '+' || *cp == '-'))
	    return (1);
	if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%'))
	    return (1);
	if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' ||
			      *cp == '~' || *cp == '^' || *cp == '"'))
	    return (1);
    }
    else if (cp[2] == 0) {
	if (what & RESTOP) {
	    if (cp[0] == '|' && cp[1] == '&')
		return (1);
	    if (cp[0] == '<' && cp[1] == '<')
		return (1);
	    if (cp[0] == '>' && cp[1] == '>')
		return (1);
	}
	if (what & EQOP) {
	    if (cp[0] == '=') {
		if (cp[1] == '=')
		    return (EQEQ);
		if (cp[1] == '~')
		    return (EQMATCH);
	    }
	    else if (cp[0] == '!') {
		if (cp[1] == '=')
		    return (NOTEQ);
		if (cp[1] == '~')
		    return (NOTEQMATCH);
	    }
	}
    }
    if (what & RELOP) {
	if (*cp == '<')
	    return (LSS);
	if (*cp == '>')
	    return (GTR);
    }
    return (0);
}

1024 1025
static tcsh_number_t
egetn(const Char *cp)
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
{
    if (*cp && *cp != '-' && !Isdigit(*cp))
	stderror(ERR_NAME | ERR_EXPRESSION);
    return (getn(cp));
}

/* Phew! */

#ifdef EDEBUG
static void
1036
etraci(const char *str, tcsh_number_t i, Char ***vp)
1037
{
1038 1039 1040 1041 1042
#ifdef HAVE_LONG_LONG
    xprintf("%s=%lld\t", str, i);
#else
    xprintf("%s=%ld\t", str, i);
#endif
1043 1044 1045 1046
    blkpr(*vp);
    xputchar('\n');
}
static void
1047
etracc(const char *str, const Char *cp, Char ***vp)
1048
{
1049
    xprintf("%s=%S\t", str, cp);
1050 1051 1052 1053
    blkpr(*vp);
    xputchar('\n');
}
#endif /* EDEBUG */