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