1/*
2 * cond.c - evaluate conditional expressions
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Paul Falstad or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Paul Falstad and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Paul Falstad and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Paul Falstad and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "zsh.mdh"
31#include "cond.pro"
32
33int tracingcond;
34
35static char *condstr[COND_MOD] = {
36    "!", "&&", "||", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
37    "-ne", "-lt", "-gt", "-le", "-ge", "=~"
38};
39
40/*
41 * Evaluate a conditional expression given the arguments.
42 * If fromtest is set, the caller is the test or [ builtin;
43 * with the pointer giving the name of the command.
44 * for POSIX conformance this supports a more limited range
45 * of functionality.
46 *
47 * Return status is the final shell status, i.e. 0 for true,
48 * 1 for false and 2 for error.
49 */
50
51/**/
52int
53evalcond(Estate state, char *fromtest)
54{
55    struct stat *st;
56    char *left, *right, *overridename, overridebuf[13];
57    Wordcode pcode;
58    wordcode code;
59    int ctype, htok = 0, ret;
60
61 rec:
62
63    left = right = overridename = NULL;
64    pcode = state->pc++;
65    code = *pcode;
66    ctype = WC_COND_TYPE(code);
67
68    switch (ctype) {
69    case COND_NOT:
70	if (tracingcond)
71	    fprintf(xtrerr, " %s", condstr[ctype]);
72	ret = evalcond(state, fromtest);
73	if (ret == 2)
74	    return ret;
75	else
76	    return !ret;
77    case COND_AND:
78	if (!(ret = evalcond(state, fromtest))) {
79	    if (tracingcond)
80		fprintf(xtrerr, " %s", condstr[ctype]);
81	    goto rec;
82	} else {
83	    state->pc = pcode + (WC_COND_SKIP(code) + 1);
84	    return ret;
85	}
86    case COND_OR:
87	if ((ret = evalcond(state, fromtest)) == 1) {
88	    if (tracingcond)
89		fprintf(xtrerr, " %s", condstr[ctype]);
90	    goto rec;
91	} else {
92	    state->pc = pcode + (WC_COND_SKIP(code) + 1);
93	    return ret;
94	}
95    case COND_REGEX:
96	{
97	    char *modname = isset(REMATCHPCRE) ? "zsh/pcre" : "zsh/regex";
98	    sprintf(overridename = overridebuf, "-%s-match", modname+4);
99	    (void)ensurefeature(modname, "C:", overridename+1);
100	    ctype = COND_MODI;
101	}
102	/*FALLTHROUGH*/
103    case COND_MOD:
104    case COND_MODI:
105	{
106	    Conddef cd;
107	    char *name = overridename, *errname;
108	    char **strs;
109	    int l = WC_COND_SKIP(code);
110
111	    if (name == NULL)
112		name = ecgetstr(state, EC_NODUP, NULL);
113	    if (ctype == COND_MOD)
114		strs = ecgetarr(state, l, EC_DUP, NULL);
115	    else {
116		char *sbuf[3];
117
118		sbuf[0] = ecgetstr(state, EC_NODUP, NULL);
119		sbuf[1] = ecgetstr(state, EC_NODUP, NULL);
120		sbuf[2] = NULL;
121
122		strs = arrdup(sbuf);
123		l = 2;
124	    }
125	    if (name && name[0] == '-')
126		errname = name;
127	    else if (strs[0] && *strs[0] == '-')
128		errname = strs[0];
129	    else
130		errname = "<null>";
131	    if (name && name[0] == '-' &&
132		(cd = getconddef((ctype == COND_MODI), name + 1, 1))) {
133		if (ctype == COND_MOD &&
134		    (l < cd->min || (cd->max >= 0 && l > cd->max))) {
135		    zwarnnam(fromtest, "unknown condition: %s", name);
136		    return 2;
137		}
138		if (tracingcond)
139		    tracemodcond(name, strs, ctype == COND_MODI);
140		return !cd->handler(strs, cd->condid);
141	    }
142	    else {
143		char *s = strs[0];
144
145		if (overridename) {
146		    /*
147		     * Standard regex function not available: this
148		     * is a hard error.
149		     */
150		    zerrnam(fromtest, "%s not available for regex",
151			     overridename);
152		    return 2;
153		}
154
155		strs[0] = dupstring(name);
156		name = s;
157
158		if (name && name[0] == '-' &&
159		    (cd = getconddef(0, name + 1, 1))) {
160		    if (l < cd->min || (cd->max >= 0 && l > cd->max)) {
161			zwarnnam(fromtest, "unknown condition: %s",
162				 errname);
163			return 2;
164		    }
165		    if (tracingcond)
166			tracemodcond(name, strs, ctype == COND_MODI);
167		    return !cd->handler(strs, cd->condid);
168		} else {
169		    zwarnnam(fromtest,
170			     "unknown condition: %s",
171			     errname);
172		}
173	    }
174	    /* module not found, error */
175	    return 2;
176	}
177    }
178    left = ecgetstr(state, EC_DUPTOK, &htok);
179    if (htok) {
180	singsub(&left);
181	untokenize(left);
182    }
183    if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRNEQ) {
184	right = ecgetstr(state, EC_DUPTOK, &htok);
185	if (htok) {
186	    singsub(&right);
187	    untokenize(right);
188	}
189    }
190    if (tracingcond) {
191	if (ctype < COND_MOD) {
192	    fputc(' ',xtrerr);
193	    quotedzputs(left, xtrerr);
194	    fprintf(xtrerr, " %s ", condstr[ctype]);
195	    if (ctype == COND_STREQ || ctype == COND_STRNEQ) {
196		char *rt = dupstring(ecrawstr(state->prog, state->pc, NULL));
197		singsub(&rt);
198		quote_tokenized_output(rt, xtrerr);
199	    }
200	    else
201		quotedzputs((char *)right, xtrerr);
202	} else {
203	    fprintf(xtrerr, " -%c ", ctype);
204	    quotedzputs(left, xtrerr);
205	}
206    }
207
208    if (ctype >= COND_EQ && ctype <= COND_GE) {
209	mnumber mn1, mn2;
210	if (fromtest) {
211	    /*
212	     * For test and [, the expressions must be base 10 integers,
213	     * not integer expressions.
214	     */
215	    char *eptr, *err;
216
217	    mn1.u.l = zstrtol(left, &eptr, 10);
218	    if (!*eptr)
219	    {
220		mn2.u.l = zstrtol(right, &eptr, 10);
221		err = right;
222	    }
223	    else
224		err = left;
225
226	    if (*eptr)
227	    {
228		zwarnnam(fromtest, "integer expression expected: %s", err);
229		return 2;
230	    }
231
232	    mn1.type = mn2.type = MN_INTEGER;
233	} else {
234	    mn1 = matheval(left);
235	    mn2 = matheval(right);
236	}
237
238	if (((mn1.type|mn2.type) & (MN_INTEGER|MN_FLOAT)) ==
239	    (MN_INTEGER|MN_FLOAT)) {
240	    /* promote to float */
241	    if (mn1.type & MN_INTEGER) {
242		mn1.type = MN_FLOAT;
243		mn1.u.d = (double)mn1.u.l;
244	    }
245	    if (mn2.type & MN_INTEGER) {
246		mn2.type = MN_FLOAT;
247		mn2.u.d = (double)mn2.u.l;
248	    }
249	}
250	switch(ctype) {
251	case COND_EQ:
252	    return !((mn1.type & MN_FLOAT) ? (mn1.u.d == mn2.u.d) :
253		     (mn1.u.l == mn2.u.l));
254	case COND_NE:
255	    return !((mn1.type & MN_FLOAT) ? (mn1.u.d != mn2.u.d) :
256		     (mn1.u.l != mn2.u.l));
257	case COND_LT:
258	    return !((mn1.type & MN_FLOAT) ? (mn1.u.d < mn2.u.d) :
259		     (mn1.u.l < mn2.u.l));
260	case COND_GT:
261	    return !((mn1.type & MN_FLOAT) ? (mn1.u.d > mn2.u.d) :
262		     (mn1.u.l > mn2.u.l));
263	case COND_LE:
264	    return !((mn1.type & MN_FLOAT) ? (mn1.u.d <= mn2.u.d) :
265		     (mn1.u.l <= mn2.u.l));
266	case COND_GE:
267	    return !((mn1.type & MN_FLOAT) ? (mn1.u.d >= mn2.u.d) :
268		     (mn1.u.l >= mn2.u.l));
269	}
270    }
271
272    switch (ctype) {
273    case COND_STREQ:
274    case COND_STRNEQ:
275	{
276	    int test, npat = state->pc[1];
277	    Patprog pprog = state->prog->pats[npat];
278
279	    if (pprog == dummy_patprog1 || pprog == dummy_patprog2) {
280		char *opat;
281		int save;
282
283		right = dupstring(opat = ecrawstr(state->prog, state->pc,
284						  &htok));
285		if (htok)
286		    singsub(&right);
287		save = (!(state->prog->flags & EF_HEAP) &&
288			!strcmp(opat, right) && pprog != dummy_patprog2);
289
290		if (!(pprog = patcompile(right, (save ? PAT_ZDUP : PAT_STATIC),
291					 NULL))) {
292		    zwarnnam(fromtest, "bad pattern: %s", right);
293		    return 2;
294		}
295		else if (save)
296		    state->prog->pats[npat] = pprog;
297	    }
298	    state->pc += 2;
299	    test = (pprog && pattry(pprog, left));
300
301	    return !(ctype == COND_STREQ ? test : !test);
302	}
303    case COND_STRLT:
304	return !(strcmp(left, right) < 0);
305    case COND_STRGTR:
306	return !(strcmp(left, right) > 0);
307    case 'e':
308    case 'a':
309	return (!doaccess(left, F_OK));
310    case 'b':
311	return (!S_ISBLK(dostat(left)));
312    case 'c':
313	return (!S_ISCHR(dostat(left)));
314    case 'd':
315	return (!S_ISDIR(dostat(left)));
316    case 'f':
317	return (!S_ISREG(dostat(left)));
318    case 'g':
319	return (!(dostat(left) & S_ISGID));
320    case 'k':
321	return (!(dostat(left) & S_ISVTX));
322    case 'n':
323	return (!strlen(left));
324    case 'o':
325	return (optison(fromtest, left));
326    case 'p':
327	return (!S_ISFIFO(dostat(left)));
328    case 'r':
329	return (!doaccess(left, R_OK));
330    case 's':
331	return !((st = getstat(left)) && !!(st->st_size));
332    case 'S':
333	return (!S_ISSOCK(dostat(left)));
334    case 'u':
335	return (!(dostat(left) & S_ISUID));
336    case 'w':
337	return (!doaccess(left, W_OK));
338    case 'x':
339	if (privasserted()) {
340	    mode_t mode = dostat(left);
341	    return !((mode & S_IXUGO) || S_ISDIR(mode));
342	}
343	return !doaccess(left, X_OK);
344    case 'z':
345	return !!(strlen(left));
346    case 'h':
347    case 'L':
348	return (!S_ISLNK(dolstat(left)));
349    case 'O':
350	return !((st = getstat(left)) && st->st_uid == geteuid());
351    case 'G':
352	return !((st = getstat(left)) && st->st_gid == getegid());
353    case 'N':
354#if defined(GET_ST_MTIME_NSEC) && defined(GET_ST_ATIME_NSEC)
355	if (!(st = getstat(left)))
356	    return 1;
357        return (st->st_atime == st->st_mtime) ?
358        	GET_ST_ATIME_NSEC(*st) > GET_ST_MTIME_NSEC(*st) :
359        	st->st_atime > st->st_mtime;
360#else
361	return !((st = getstat(left)) && st->st_atime <= st->st_mtime);
362#endif
363    case 't':
364	return !isatty(mathevali(left));
365    case COND_NT:
366    case COND_OT:
367	{
368	    time_t a;
369#ifdef GET_ST_MTIME_NSEC
370	    long nsecs;
371#endif
372
373	    if (!(st = getstat(left)))
374		return 1;
375	    a = st->st_mtime;
376#ifdef GET_ST_MTIME_NSEC
377	    nsecs = GET_ST_MTIME_NSEC(*st);
378#endif
379	    if (!(st = getstat(right)))
380		return 1;
381#ifdef GET_ST_MTIME_NSEC
382	    if (a == st->st_mtime) {
383                return !((ctype == COND_NT) ? nsecs > GET_ST_MTIME_NSEC(*st) :
384                        nsecs < GET_ST_MTIME_NSEC(*st));
385	    }
386#endif
387	    return !((ctype == COND_NT) ? a > st->st_mtime : a < st->st_mtime);
388	}
389    case COND_EF:
390	{
391	    dev_t d;
392	    ino_t i;
393
394	    if (!(st = getstat(left)))
395		return 1;
396	    d = st->st_dev;
397	    i = st->st_ino;
398	    if (!(st = getstat(right)))
399		return 1;
400	    return !(d == st->st_dev && i == st->st_ino);
401	}
402    default:
403	zwarnnam(fromtest, "bad cond code");
404	return 2;
405    }
406}
407
408
409/**/
410static int
411doaccess(char *s, int c)
412{
413#ifdef HAVE_FACCESSX
414    if (!strncmp(s, "/dev/fd/", 8))
415	return !faccessx(atoi(s + 8), c, ACC_SELF);
416#endif
417    return !access(unmeta(s), c);
418}
419
420
421static struct stat st;
422
423/**/
424static struct stat *
425getstat(char *s)
426{
427    char *us;
428
429/* /dev/fd/n refers to the open file descriptor n.  We always use fstat *
430 * in this case since on Solaris /dev/fd/n is a device special file     */
431    if (!strncmp(s, "/dev/fd/", 8)) {
432	if (fstat(atoi(s + 8), &st))
433	    return NULL;
434        return &st;
435    }
436
437    if (!(us = unmeta(s)))
438        return NULL;
439    if (stat(us, &st))
440	return NULL;
441    return &st;
442}
443
444
445/**/
446static mode_t
447dostat(char *s)
448{
449    struct stat *statp;
450
451    if (!(statp = getstat(s)))
452	return 0;
453    return statp->st_mode;
454}
455
456
457/* pem@aaii.oz; needed since dostat now uses "stat" */
458
459/**/
460static mode_t
461dolstat(char *s)
462{
463    if (lstat(unmeta(s), &st) < 0)
464	return 0;
465    return st.st_mode;
466}
467
468
469/*
470 * optison returns evalcond-friendly statuses (true, false, error).
471 */
472
473/**/
474static int
475optison(char *name, char *s)
476{
477    int i;
478
479    if (strlen(s) == 1)
480	i = optlookupc(*s);
481    else
482	i = optlookup(s);
483    if (!i) {
484	zwarnnam(name, "no such option: %s", s);
485	return 2;
486    } else if(i < 0)
487	return !unset(-i);
488    else
489	return !isset(i);
490}
491
492/**/
493mod_export char *
494cond_str(char **args, int num, int raw)
495{
496    char *s = args[num];
497
498    if (has_token(s)) {
499	singsub(&s);
500	if (!raw)
501	    untokenize(s);
502    }
503    return s;
504}
505
506/**/
507mod_export zlong
508cond_val(char **args, int num)
509{
510    char *s = args[num];
511
512    if (has_token(s)) {
513	singsub(&s);
514	untokenize(s);
515    }
516    return mathevali(s);
517}
518
519/**/
520mod_export int
521cond_match(char **args, int num, char *str)
522{
523    char *s = args[num];
524
525    singsub(&s);
526
527    return matchpat(str, s);
528}
529
530/**/
531static void
532tracemodcond(char *name, char **args, int inf)
533{
534    char **aptr;
535
536    args = arrdup(args);
537    for (aptr = args; *aptr; aptr++)
538	untokenize(*aptr);
539    if (inf) {
540	fprintf(xtrerr, " %s %s %s", args[0], name, args[1]);
541    } else {
542	fprintf(xtrerr, " %s", name);
543	while (*args)
544	    fprintf(xtrerr, " %s", *args++);
545    }
546}
547