1
0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-11-19 13:33:52 +01:00
zsh/Src/cond.c

501 lines
11 KiB
C
Raw Normal View History

1999-04-15 20:05:38 +02:00
/*
* cond.c - evaluate conditional expressions
*
* This file is part of zsh, the Z shell.
*
* Copyright (c) 1992-1997 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.mdh"
#include "cond.pro"
2000-04-01 22:49:47 +02:00
int tracingcond;
static char *condstr[COND_MOD] = {
"!", "&&", "||", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
"-ne", "-lt", "-gt", "-le", "-ge"
};
/*
* Evaluate a conditional expression given the arguments.
* If fromtest is set, the caller is the test or [ builtin;
* with the pointer giving the name of the command.
* for POSIX conformance this supports a more limited range
* of functionality.
*
* Return status is the final shell status, i.e. 0 for true,
* 1 for false and 2 for error.
*/
1999-04-15 20:05:38 +02:00
/**/
int
evalcond(Estate state, char *fromtest)
1999-04-15 20:05:38 +02:00
{
struct stat *st;
2000-04-01 22:49:47 +02:00
char *left, *right;
Wordcode pcode;
wordcode code;
int ctype, htok = 0, ret;
2000-04-01 22:49:47 +02:00
rec:
left = right = NULL;
pcode = state->pc++;
code = *pcode;
ctype = WC_COND_TYPE(code);
1999-04-15 20:05:38 +02:00
2000-04-01 22:49:47 +02:00
switch (ctype) {
1999-04-15 20:05:38 +02:00
case COND_NOT:
2000-04-01 22:49:47 +02:00
if (tracingcond)
fprintf(xtrerr, " %s", condstr[ctype]);
ret = evalcond(state, fromtest);
if (ret == 2)
return ret;
else
return !ret;
1999-04-15 20:05:38 +02:00
case COND_AND:
if (!(ret = evalcond(state, fromtest))) {
2000-04-01 22:49:47 +02:00
if (tracingcond)
fprintf(xtrerr, " %s", condstr[ctype]);
goto rec;
} else {
state->pc = pcode + (WC_COND_SKIP(code) + 1);
return ret;
2000-04-01 22:49:47 +02:00
}
1999-04-15 20:05:38 +02:00
case COND_OR:
if ((ret = evalcond(state, fromtest)) == 1) {
2000-04-01 22:49:47 +02:00
if (tracingcond)
fprintf(xtrerr, " %s", condstr[ctype]);
goto rec;
} else {
state->pc = pcode + (WC_COND_SKIP(code) + 1);
return ret;
2000-04-01 22:49:47 +02:00
}
case COND_MOD:
case COND_MODI:
{
Conddef cd;
char *name = ecgetstr(state, EC_NODUP, NULL), **strs;
int l = WC_COND_SKIP(code);
if (ctype == COND_MOD)
strs = ecgetarr(state, l, EC_DUP, NULL);
else {
char *sbuf[3];
sbuf[0] = ecgetstr(state, EC_NODUP, NULL);
sbuf[1] = ecgetstr(state, EC_NODUP, NULL);
sbuf[2] = NULL;
strs = arrdup(sbuf);
l = 2;
}
if ((cd = getconddef((ctype == COND_MODI), name + 1, 1))) {
if (ctype == COND_MOD &&
(l < cd->min || (cd->max >= 0 && l > cd->max))) {
zwarnnam(fromtest, "unrecognized condition: `%s'",
name, 0);
return 2;
2000-04-01 22:49:47 +02:00
}
if (tracingcond)
tracemodcond(name, strs, ctype == COND_MODI);
return !cd->handler(strs, cd->condid);
2000-04-01 22:49:47 +02:00
}
else {
char *s = strs[0];
strs[0] = dupstring(name);
name = s;
if (name && name[0] == '-' &&
(cd = getconddef(0, name + 1, 1))) {
if (l < cd->min || (cd->max >= 0 && l > cd->max)) {
zwarnnam(fromtest, "unrecognized condition: `%s'",
name, 0);
return 2;
2000-04-01 22:49:47 +02:00
}
if (tracingcond)
tracemodcond(name, strs, ctype == COND_MODI);
return !cd->handler(strs, cd->condid);
} else {
zwarnnam(fromtest,
"unrecognized condition: `%s'", name, 0);
}
2000-04-01 22:49:47 +02:00
}
/* module not found, error */
return 2;
2000-04-01 22:49:47 +02:00
}
1999-04-15 20:05:38 +02:00
}
2000-04-01 22:49:47 +02:00
left = ecgetstr(state, EC_DUPTOK, &htok);
if (htok) {
singsub(&left);
untokenize(left);
1999-04-15 20:05:38 +02:00
}
2000-04-01 22:49:47 +02:00
if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRNEQ) {
right = ecgetstr(state, EC_DUPTOK, &htok);
if (htok) {
singsub(&right);
untokenize(right);
}
}
if (tracingcond) {
if (ctype < COND_MOD) {
char *rt = (char *) right;
if (ctype == COND_STREQ || ctype == COND_STRNEQ) {
rt = dupstring(ecrawstr(state->prog, state->pc, NULL));
singsub(&rt);
untokenize(rt);
}
fputc(' ',xtrerr);
quotedzputs(left, xtrerr);
fprintf(xtrerr, " %s ", condstr[ctype]);
quotedzputs(rt, xtrerr);
} else {
fprintf(xtrerr, " -%c ", ctype);
quotedzputs(left, xtrerr);
}
2000-04-01 22:49:47 +02:00
}
if (ctype >= COND_EQ && ctype <= COND_GE) {
mnumber mn1, mn2;
if (fromtest) {
/*
* For test and [, the expressions must be base 10 integers,
* not integer expressions.
*/
char *eptr, *err;
mn1.u.l = zstrtol(left, &eptr, 10);
if (!*eptr)
{
mn2.u.l = zstrtol(right, &eptr, 10);
err = right;
}
else
err = left;
if (*eptr)
{
zwarnnam(fromtest, "integer expression expected: %s",
err, 0);
return 2;
}
mn1.type = mn2.type = MN_INTEGER;
} else {
mn1 = matheval(left);
mn2 = matheval(right);
}
2000-04-01 22:49:47 +02:00
if (((mn1.type|mn2.type) & (MN_INTEGER|MN_FLOAT)) ==
(MN_INTEGER|MN_FLOAT)) {
/* promote to float */
if (mn1.type & MN_INTEGER) {
mn1.type = MN_FLOAT;
mn1.u.d = (double)mn1.u.l;
}
if (mn2.type & MN_INTEGER) {
mn2.type = MN_FLOAT;
mn2.u.d = (double)mn2.u.l;
}
}
switch(ctype) {
case COND_EQ:
return !((mn1.type & MN_FLOAT) ? (mn1.u.d == mn2.u.d) :
(mn1.u.l == mn2.u.l));
2000-04-01 22:49:47 +02:00
case COND_NE:
return !((mn1.type & MN_FLOAT) ? (mn1.u.d != mn2.u.d) :
(mn1.u.l != mn2.u.l));
2000-04-01 22:49:47 +02:00
case COND_LT:
return !((mn1.type & MN_FLOAT) ? (mn1.u.d < mn2.u.d) :
(mn1.u.l < mn2.u.l));
2000-04-01 22:49:47 +02:00
case COND_GT:
return !((mn1.type & MN_FLOAT) ? (mn1.u.d > mn2.u.d) :
(mn1.u.l > mn2.u.l));
2000-04-01 22:49:47 +02:00
case COND_LE:
return !((mn1.type & MN_FLOAT) ? (mn1.u.d <= mn2.u.d) :
(mn1.u.l <= mn2.u.l));
2000-04-01 22:49:47 +02:00
case COND_GE:
return !((mn1.type & MN_FLOAT) ? (mn1.u.d >= mn2.u.d) :
(mn1.u.l >= mn2.u.l));
2000-04-01 22:49:47 +02:00
}
}
switch (ctype) {
1999-04-15 20:05:38 +02:00
case COND_STREQ:
case COND_STRNEQ:
2000-04-01 22:49:47 +02:00
{
int test, npat = state->pc[1];
Patprog pprog = state->prog->pats[npat];
if (pprog == dummy_patprog1 || pprog == dummy_patprog2) {
char *opat;
int save;
right = dupstring(opat = ecrawstr(state->prog, state->pc,
2000-04-01 22:49:47 +02:00
&htok));
if (htok)
singsub(&right);
save = (!(state->prog->flags & EF_HEAP) &&
!strcmp(opat, right) && pprog != dummy_patprog2);
if (!(pprog = patcompile(right, (save ? PAT_ZDUP : PAT_STATIC),
NULL))) {
zwarnnam(fromtest, "bad pattern: %s", right, 0);
return 2;
}
2000-04-01 22:49:47 +02:00
else if (save)
state->prog->pats[npat] = pprog;
}
state->pc += 2;
test = (pprog && pattry(pprog, left));
return !(ctype == COND_STREQ ? test : !test);
2000-04-01 22:49:47 +02:00
}
1999-04-15 20:05:38 +02:00
case COND_STRLT:
return !(strcmp(left, right) < 0);
1999-04-15 20:05:38 +02:00
case COND_STRGTR:
return !(strcmp(left, right) > 0);
1999-04-15 20:05:38 +02:00
case 'e':
case 'a':
return (!doaccess(left, F_OK));
1999-04-15 20:05:38 +02:00
case 'b':
return (!S_ISBLK(dostat(left)));
1999-04-15 20:05:38 +02:00
case 'c':
return (!S_ISCHR(dostat(left)));
1999-04-15 20:05:38 +02:00
case 'd':
return (!S_ISDIR(dostat(left)));
1999-04-15 20:05:38 +02:00
case 'f':
return (!S_ISREG(dostat(left)));
1999-04-15 20:05:38 +02:00
case 'g':
return (!(dostat(left) & S_ISGID));
1999-04-15 20:05:38 +02:00
case 'k':
return (!(dostat(left) & S_ISVTX));
1999-04-15 20:05:38 +02:00
case 'n':
return (!strlen(left));
1999-04-15 20:05:38 +02:00
case 'o':
return (optison(fromtest, left));
1999-04-15 20:05:38 +02:00
case 'p':
return (!S_ISFIFO(dostat(left)));
1999-04-15 20:05:38 +02:00
case 'r':
return (!doaccess(left, R_OK));
1999-04-15 20:05:38 +02:00
case 's':
return !((st = getstat(left)) && !!(st->st_size));
1999-04-15 20:05:38 +02:00
case 'S':
return (!S_ISSOCK(dostat(left)));
1999-04-15 20:05:38 +02:00
case 'u':
return (!(dostat(left) & S_ISUID));
1999-04-15 20:05:38 +02:00
case 'w':
return (!doaccess(left, W_OK));
1999-04-15 20:05:38 +02:00
case 'x':
if (privasserted()) {
2000-04-01 22:49:47 +02:00
mode_t mode = dostat(left);
return !((mode & S_IXUGO) || S_ISDIR(mode));
1999-04-15 20:05:38 +02:00
}
return !doaccess(left, X_OK);
1999-04-15 20:05:38 +02:00
case 'z':
return !!(strlen(left));
1999-04-15 20:05:38 +02:00
case 'h':
case 'L':
return (!S_ISLNK(dolstat(left)));
1999-04-15 20:05:38 +02:00
case 'O':
return !((st = getstat(left)) && st->st_uid == geteuid());
1999-04-15 20:05:38 +02:00
case 'G':
return !((st = getstat(left)) && st->st_gid == getegid());
1999-04-15 20:05:38 +02:00
case 'N':
return !((st = getstat(left)) && st->st_atime <= st->st_mtime);
1999-04-15 20:05:38 +02:00
case 't':
return !isatty(mathevali(left));
1999-04-15 20:05:38 +02:00
case COND_NT:
case COND_OT:
{
time_t a;
2000-04-01 22:49:47 +02:00
if (!(st = getstat(left)))
return 1;
1999-04-15 20:05:38 +02:00
a = st->st_mtime;
2000-04-01 22:49:47 +02:00
if (!(st = getstat(right)))
return 2;
return !((ctype == COND_NT) ? a > st->st_mtime : a < st->st_mtime);
1999-04-15 20:05:38 +02:00
}
case COND_EF:
{
dev_t d;
ino_t i;
2000-04-01 22:49:47 +02:00
if (!(st = getstat(left)))
return 1;
1999-04-15 20:05:38 +02:00
d = st->st_dev;
i = st->st_ino;
2000-04-01 22:49:47 +02:00
if (!(st = getstat(right)))
return 1;
return !(d == st->st_dev && i == st->st_ino);
1999-04-15 20:05:38 +02:00
}
default:
zwarnnam(fromtest, "bad cond code", NULL, 0);
return 2;
1999-04-15 20:05:38 +02:00
}
return 1;
1999-04-15 20:05:38 +02:00
}
/**/
static int
doaccess(char *s, int c)
{
2000-04-01 22:49:47 +02:00
#ifdef HAVE_FACCESSX
if (!strncmp(s, "/dev/fd/", 8))
return !faccessx(atoi(s + 8), c, ACC_SELF);
#endif
1999-04-15 20:05:38 +02:00
return !access(unmeta(s), c);
}
static struct stat st;
/**/
static struct stat *
getstat(char *s)
{
char *us;
1999-04-15 20:05:38 +02:00
/* /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 (!(us = unmeta(s)))
return NULL;
if (stat(us, &st))
1999-04-15 20:05:38 +02:00
return NULL;
return &st;
}
/**/
static mode_t
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" */
/**/
static mode_t
dolstat(char *s)
{
if (lstat(unmeta(s), &st) < 0)
return 0;
return st.st_mode;
}
/*
* optison returns evalcond-friendly statuses (true, false, error).
*/
1999-04-15 20:05:38 +02:00
/**/
static int
optison(char *name, char *s)
1999-04-15 20:05:38 +02:00
{
int i;
if (strlen(s) == 1)
i = optlookupc(*s);
else
i = optlookup(s);
if (!i) {
zwarnnam(name, "no such option: %s", s, 0);
return 2;
1999-04-15 20:05:38 +02:00
} else if(i < 0)
return !unset(-i);
1999-04-15 20:05:38 +02:00
else
return !isset(i);
1999-04-15 20:05:38 +02:00
}
2000-04-01 22:49:47 +02:00
/**/
mod_export char *
cond_str(char **args, int num, int raw)
{
char *s = args[num];
if (has_token(s)) {
singsub(&s);
if (!raw)
untokenize(s);
}
return s;
}
/**/
mod_export zlong
cond_val(char **args, int num)
{
char *s = args[num];
if (has_token(s)) {
singsub(&s);
untokenize(s);
}
return mathevali(s);
}
/**/
mod_export int
cond_match(char **args, int num, char *str)
{
char *s = args[num];
singsub(&s);
return matchpat(str, s);
}
/**/
static void
tracemodcond(char *name, char **args, int inf)
{
char **aptr;
args = arrdup(args);
for (aptr = args; *aptr; aptr++)
untokenize(*aptr);
if (inf) {
fprintf(xtrerr, " %s %s %s", args[0], name, args[1]);
} else {
fprintf(xtrerr, " %s", name);
while (*args)
fprintf(xtrerr, " %s", *args++);
}
}