/*
 * cond.c - evaluate conditional expressions
 *
 * This file is part of zsh, the Z shell.
 *
 * Copyright (c) 1992-1996 Paul Falstad
 * All rights reserved.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and to distribute modified versions of this software for any
 * purpose, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * In no event shall Paul Falstad or the Zsh Development Group be liable
 * to any party for direct, indirect, special, incidental, or consequential
 * damages arising out of the use of this software and its documentation,
 * even if Paul Falstad and the Zsh Development Group have been advised of
 * the possibility of such damage.
 *
 * Paul Falstad and the Zsh Development Group specifically disclaim any
 * warranties, including, but not limited to, the implied warranties of
 * merchantability and fitness for a particular purpose.  The software
 * provided hereunder is on an "as is" basis, and Paul Falstad and the
 * Zsh Development Group have no obligation to provide maintenance,
 * support, updates, enhancements, or modifications.
 *
 */

#include "zsh.h"

/**/
int
evalcond(Cond c)
{
    struct stat *st;

    switch (c->type) {
    case COND_NOT:
	return !evalcond(c->left);
    case COND_AND:
	return evalcond(c->left) && evalcond(c->right);
    case COND_OR:
	return evalcond(c->left) || evalcond(c->right);
    }
    singsub((char **)&c->left);
    untokenize(c->left);
    if (c->right) {
	singsub((char **)&c->right);
	if (c->type != COND_STREQ && c->type != COND_STRNEQ)
	    untokenize(c->right);
    }
    switch (c->type) {
    case COND_STREQ:
	return matchpat(c->left, c->right);
    case COND_STRNEQ:
	return !matchpat(c->left, c->right);
    case COND_STRLT:
	return strcmp(c->left, c->right) < 0;
    case COND_STRGTR:
	return strcmp(c->left, c->right) > 0;
    case 'e':
    case 'a':
	return (doaccess(c->left, F_OK));
    case 'b':
	return (S_ISBLK(dostat(c->left)));
    case 'c':
	return (S_ISCHR(dostat(c->left)));
    case 'd':
	return (S_ISDIR(dostat(c->left)));
    case 'f':
	return (S_ISREG(dostat(c->left)));
    case 'g':
	return (!!(dostat(c->left) & S_ISGID));
    case 'k':
	return (!!(dostat(c->left) & S_ISVTX));
    case 'n':
	return (!!strlen(c->left));
    case 'o':
	return (optison(c->left));
    case 'p':
	return (S_ISFIFO(dostat(c->left)));
    case 'r':
	return (doaccess(c->left, R_OK));
    case 's':
	return ((st = getstat(c->left)) && !!(st->st_size));
    case 'S':
#ifdef S_ISSOCK
	return (S_ISSOCK(dostat(c->left)));
#else
	return 0;   /* some versions of SCO are missing S_ISSOCK */
#endif
    case 'u':
	return (!!(dostat(c->left) & S_ISUID));
    case 'w':
	return (doaccess(c->left, W_OK));
    case 'x':
	if (!geteuid()) {
	    unsigned short mode = dostat(c->left);
	    return (mode & 0111) || S_ISDIR(mode);
	}
	return doaccess(c->left, X_OK);
    case 'z':
	return (!strlen(c->left));
    case 'h':
    case 'L':
	return (S_ISLNK(dolstat(c->left)));
    case 'O':
	return ((st = getstat(c->left)) && st->st_uid == geteuid());
    case 'G':
	return ((st = getstat(c->left)) && st->st_gid == getegid());
    case 'N':
	return ((st = getstat(c->left)) && st->st_atime <= st->st_mtime);
    case 't':
	return isatty(matheval(c->left));
    case COND_EQ:
	return matheval(c->left) == matheval(c->right);
    case COND_NE:
	return matheval(c->left) != matheval(c->right);
    case COND_LT:
	return matheval(c->left) < matheval(c->right);
    case COND_GT:
	return matheval(c->left) > matheval(c->right);
    case COND_LE:
	return matheval(c->left) <= matheval(c->right);
    case COND_GE:
	return matheval(c->left) >= matheval(c->right);
    case COND_NT:
    case COND_OT:
	{
	    time_t a;

	    if (!(st = getstat(c->left)))
		return 0;
	    a = st->st_mtime;
	    if (!(st = getstat(c->right)))
		return 0;
	    return (c->type == COND_NT) ? a > st->st_mtime : a < st->st_mtime;
	}
    case COND_EF:
	{
	    dev_t d;
	    ino_t i;

	    if (!(st = getstat(c->left)))
		return 0;
	    d = st->st_dev;
	    i = st->st_ino;
	    if (!(st = getstat(c->right)))
		return 0;
	    return d == st->st_dev && i == st->st_ino;
	}
    default:
	zerr("bad cond structure", NULL, 0);
    }
    return 0;
}


/**/
int
doaccess(char *s, int c)
{
    return !access(unmeta(s), c);
}


static struct stat st;

/**/
struct stat *
getstat(char *s)
{
/* /dev/fd/n refers to the open file descriptor n.  We always use fstat *
 * in this case since on Solaris /dev/fd/n is a device special file     */
    if (!strncmp(s, "/dev/fd/", 8)) {
	if (fstat(atoi(s + 8), &st))
	    return NULL;
        return &st;
    }

    if (stat(unmeta(s), &st))
	return NULL;
    return &st;
}


/**/
unsigned short
dostat(char *s)
{
    struct stat *statp;

    if (!(statp = getstat(s)))
	return 0;
    return statp->st_mode;
}


/* pem@aaii.oz; needed since dostat now uses "stat" */

/**/
unsigned short
dolstat(char *s)
{
    if (lstat(unmeta(s), &st) < 0)
	return 0;
    return st.st_mode;
}


/**/
int
optison(char *s)
{
    int i;

    if (strlen(s) == 1)
	i = optlookupc(*s);
    else
	i = optlookup(s);
    if (!i) {
	zerr("no such option: %s", s, 0);
	return 0;
    } else if(i < 0)
	return unset(-i);
    else
	return isset(i);
}
