|
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 int tracingcond; |
|
34 |
|
35 static 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 /**/ |
|
52 int |
|
53 evalcond(Estate state, char *fromtest) |
|
54 { |
|
55 struct stat *st; |
|
56 char *left, *right; |
|
57 Wordcode pcode; |
|
58 wordcode code; |
|
59 int ctype, htok = 0, ret; |
|
60 |
|
61 rec: |
|
62 |
|
63 left = right = 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_MOD: |
|
96 case COND_MODI: |
|
97 { |
|
98 Conddef cd; |
|
99 char *name = ecgetstr(state, EC_NODUP, NULL), **strs; |
|
100 int l = WC_COND_SKIP(code); |
|
101 |
|
102 if (ctype == COND_MOD) |
|
103 strs = ecgetarr(state, l, EC_DUP, NULL); |
|
104 else { |
|
105 char *sbuf[3]; |
|
106 |
|
107 sbuf[0] = ecgetstr(state, EC_NODUP, NULL); |
|
108 sbuf[1] = ecgetstr(state, EC_NODUP, NULL); |
|
109 sbuf[2] = NULL; |
|
110 |
|
111 strs = arrdup(sbuf); |
|
112 l = 2; |
|
113 } |
|
114 if ((cd = getconddef((ctype == COND_MODI), name + 1, 1))) { |
|
115 if (ctype == COND_MOD && |
|
116 (l < cd->min || (cd->max >= 0 && l > cd->max))) { |
|
117 zwarnnam(fromtest, "unrecognized condition: `%s'", |
|
118 name, 0); |
|
119 return 2; |
|
120 } |
|
121 if (tracingcond) |
|
122 tracemodcond(name, strs, ctype == COND_MODI); |
|
123 return !cd->handler(strs, cd->condid); |
|
124 } |
|
125 else { |
|
126 char *s = strs[0]; |
|
127 |
|
128 strs[0] = dupstring(name); |
|
129 name = s; |
|
130 |
|
131 if (name && name[0] == '-' && |
|
132 (cd = getconddef(0, name + 1, 1))) { |
|
133 if (l < cd->min || (cd->max >= 0 && l > cd->max)) { |
|
134 zwarnnam(fromtest, "unrecognized condition: `%s'", |
|
135 name, 0); |
|
136 return 2; |
|
137 } |
|
138 if (tracingcond) |
|
139 tracemodcond(name, strs, ctype == COND_MODI); |
|
140 return !cd->handler(strs, cd->condid); |
|
141 } else { |
|
142 zwarnnam(fromtest, |
|
143 "unrecognized condition: `%s'", name, 0); |
|
144 } |
|
145 } |
|
146 /* module not found, error */ |
|
147 return 2; |
|
148 } |
|
149 } |
|
150 left = ecgetstr(state, EC_DUPTOK, &htok); |
|
151 if (htok) { |
|
152 singsub(&left); |
|
153 untokenize(left); |
|
154 } |
|
155 if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRNEQ) { |
|
156 right = ecgetstr(state, EC_DUPTOK, &htok); |
|
157 if (htok) { |
|
158 singsub(&right); |
|
159 untokenize(right); |
|
160 } |
|
161 } |
|
162 if (tracingcond) { |
|
163 if (ctype < COND_MOD) { |
|
164 char *rt = (char *) right; |
|
165 if (ctype == COND_STREQ || ctype == COND_STRNEQ) { |
|
166 rt = dupstring(ecrawstr(state->prog, state->pc, NULL)); |
|
167 singsub(&rt); |
|
168 untokenize(rt); |
|
169 } |
|
170 fputc(' ',xtrerr); |
|
171 quotedzputs(left, xtrerr); |
|
172 fprintf(xtrerr, " %s ", condstr[ctype]); |
|
173 quotedzputs(rt, xtrerr); |
|
174 } else { |
|
175 fprintf(xtrerr, " -%c ", ctype); |
|
176 quotedzputs(left, xtrerr); |
|
177 } |
|
178 } |
|
179 |
|
180 if (ctype >= COND_EQ && ctype <= COND_GE) { |
|
181 mnumber mn1, mn2; |
|
182 if (fromtest) { |
|
183 /* |
|
184 * For test and [, the expressions must be base 10 integers, |
|
185 * not integer expressions. |
|
186 */ |
|
187 char *eptr, *err; |
|
188 |
|
189 mn1.u.l = zstrtol(left, &eptr, 10); |
|
190 if (!*eptr) |
|
191 { |
|
192 mn2.u.l = zstrtol(right, &eptr, 10); |
|
193 err = right; |
|
194 } |
|
195 else |
|
196 err = left; |
|
197 |
|
198 if (*eptr) |
|
199 { |
|
200 zwarnnam(fromtest, "integer expression expected: %s", |
|
201 err, 0); |
|
202 return 2; |
|
203 } |
|
204 |
|
205 mn1.type = mn2.type = MN_INTEGER; |
|
206 } else { |
|
207 mn1 = matheval(left); |
|
208 mn2 = matheval(right); |
|
209 } |
|
210 |
|
211 if (((mn1.type|mn2.type) & (MN_INTEGER|MN_FLOAT)) == |
|
212 (MN_INTEGER|MN_FLOAT)) { |
|
213 /* promote to float */ |
|
214 if (mn1.type & MN_INTEGER) { |
|
215 mn1.type = MN_FLOAT; |
|
216 mn1.u.d = (double)mn1.u.l; |
|
217 } |
|
218 if (mn2.type & MN_INTEGER) { |
|
219 mn2.type = MN_FLOAT; |
|
220 mn2.u.d = (double)mn2.u.l; |
|
221 } |
|
222 } |
|
223 switch(ctype) { |
|
224 case COND_EQ: |
|
225 return !((mn1.type & MN_FLOAT) ? (mn1.u.d == mn2.u.d) : |
|
226 (mn1.u.l == mn2.u.l)); |
|
227 case COND_NE: |
|
228 return !((mn1.type & MN_FLOAT) ? (mn1.u.d != mn2.u.d) : |
|
229 (mn1.u.l != mn2.u.l)); |
|
230 case COND_LT: |
|
231 return !((mn1.type & MN_FLOAT) ? (mn1.u.d < mn2.u.d) : |
|
232 (mn1.u.l < mn2.u.l)); |
|
233 case COND_GT: |
|
234 return !((mn1.type & MN_FLOAT) ? (mn1.u.d > mn2.u.d) : |
|
235 (mn1.u.l > mn2.u.l)); |
|
236 case COND_LE: |
|
237 return !((mn1.type & MN_FLOAT) ? (mn1.u.d <= mn2.u.d) : |
|
238 (mn1.u.l <= mn2.u.l)); |
|
239 case COND_GE: |
|
240 return !((mn1.type & MN_FLOAT) ? (mn1.u.d >= mn2.u.d) : |
|
241 (mn1.u.l >= mn2.u.l)); |
|
242 } |
|
243 } |
|
244 |
|
245 switch (ctype) { |
|
246 case COND_STREQ: |
|
247 case COND_STRNEQ: |
|
248 { |
|
249 int test, npat = state->pc[1]; |
|
250 Patprog pprog = state->prog->pats[npat]; |
|
251 |
|
252 if (pprog == dummy_patprog1 || pprog == dummy_patprog2) { |
|
253 char *opat; |
|
254 int save; |
|
255 |
|
256 right = dupstring(opat = ecrawstr(state->prog, state->pc, |
|
257 &htok)); |
|
258 if (htok) |
|
259 singsub(&right); |
|
260 save = (!(state->prog->flags & EF_HEAP) && |
|
261 !strcmp(opat, right) && pprog != dummy_patprog2); |
|
262 |
|
263 if (!(pprog = patcompile(right, (save ? PAT_ZDUP : PAT_STATIC), |
|
264 NULL))) { |
|
265 zwarnnam(fromtest, "bad pattern: %s", right, 0); |
|
266 return 2; |
|
267 } |
|
268 else if (save) |
|
269 state->prog->pats[npat] = pprog; |
|
270 } |
|
271 state->pc += 2; |
|
272 test = (pprog && pattry(pprog, left)); |
|
273 |
|
274 return !(ctype == COND_STREQ ? test : !test); |
|
275 } |
|
276 case COND_STRLT: |
|
277 return !(strcmp(left, right) < 0); |
|
278 case COND_STRGTR: |
|
279 return !(strcmp(left, right) > 0); |
|
280 case 'e': |
|
281 case 'a': |
|
282 return (!doaccess(left, F_OK)); |
|
283 case 'b': |
|
284 return (!S_ISBLK(dostat(left))); |
|
285 case 'c': |
|
286 return (!S_ISCHR(dostat(left))); |
|
287 case 'd': |
|
288 return (!S_ISDIR(dostat(left))); |
|
289 case 'f': |
|
290 return (!S_ISREG(dostat(left))); |
|
291 case 'g': |
|
292 return (!(dostat(left) & S_ISGID)); |
|
293 case 'k': |
|
294 return (!(dostat(left) & S_ISVTX)); |
|
295 case 'n': |
|
296 return (!strlen(left)); |
|
297 case 'o': |
|
298 return (optison(fromtest, left)); |
|
299 case 'p': |
|
300 return (!S_ISFIFO(dostat(left))); |
|
301 case 'r': |
|
302 return (!doaccess(left, R_OK)); |
|
303 case 's': |
|
304 return !((st = getstat(left)) && !!(st->st_size)); |
|
305 case 'S': |
|
306 return (!S_ISSOCK(dostat(left))); |
|
307 case 'u': |
|
308 return (!(dostat(left) & S_ISUID)); |
|
309 case 'w': |
|
310 return (!doaccess(left, W_OK)); |
|
311 case 'x': |
|
312 if (privasserted()) { |
|
313 mode_t mode = dostat(left); |
|
314 return !((mode & S_IXUGO) || S_ISDIR(mode)); |
|
315 } |
|
316 return !doaccess(left, X_OK); |
|
317 case 'z': |
|
318 return !!(strlen(left)); |
|
319 case 'h': |
|
320 case 'L': |
|
321 return (!S_ISLNK(dolstat(left))); |
|
322 case 'O': |
|
323 return !((st = getstat(left)) && st->st_uid == geteuid()); |
|
324 case 'G': |
|
325 return !((st = getstat(left)) && st->st_gid == getegid()); |
|
326 case 'N': |
|
327 return !((st = getstat(left)) && st->st_atime <= st->st_mtime); |
|
328 case 't': |
|
329 return !isatty(mathevali(left)); |
|
330 case COND_NT: |
|
331 case COND_OT: |
|
332 { |
|
333 time_t a; |
|
334 |
|
335 if (!(st = getstat(left))) |
|
336 return 1; |
|
337 a = st->st_mtime; |
|
338 if (!(st = getstat(right))) |
|
339 return 1; |
|
340 return !((ctype == COND_NT) ? a > st->st_mtime : a < st->st_mtime); |
|
341 } |
|
342 case COND_EF: |
|
343 { |
|
344 dev_t d; |
|
345 ino_t i; |
|
346 |
|
347 if (!(st = getstat(left))) |
|
348 return 1; |
|
349 d = st->st_dev; |
|
350 i = st->st_ino; |
|
351 if (!(st = getstat(right))) |
|
352 return 1; |
|
353 return !(d == st->st_dev && i == st->st_ino); |
|
354 } |
|
355 default: |
|
356 zwarnnam(fromtest, "bad cond code", NULL, 0); |
|
357 return 2; |
|
358 } |
|
359 return 1; |
|
360 } |
|
361 |
|
362 |
|
363 /**/ |
|
364 static int |
|
365 doaccess(char *s, int c) |
|
366 { |
|
367 #ifdef HAVE_FACCESSX |
|
368 if (!strncmp(s, "/dev/fd/", 8)) |
|
369 return !faccessx(atoi(s + 8), c, ACC_SELF); |
|
370 #endif |
|
371 return !access(unmeta(s), c); |
|
372 } |
|
373 |
|
374 |
|
375 static struct stat st; |
|
376 |
|
377 /**/ |
|
378 static struct stat * |
|
379 getstat(char *s) |
|
380 { |
|
381 char *us; |
|
382 |
|
383 /* /dev/fd/n refers to the open file descriptor n. We always use fstat * |
|
384 * in this case since on Solaris /dev/fd/n is a device special file */ |
|
385 if (!strncmp(s, "/dev/fd/", 8)) { |
|
386 if (fstat(atoi(s + 8), &st)) |
|
387 return NULL; |
|
388 return &st; |
|
389 } |
|
390 |
|
391 if (!(us = unmeta(s))) |
|
392 return NULL; |
|
393 if (stat(us, &st)) |
|
394 return NULL; |
|
395 return &st; |
|
396 } |
|
397 |
|
398 |
|
399 /**/ |
|
400 static mode_t |
|
401 dostat(char *s) |
|
402 { |
|
403 struct stat *statp; |
|
404 |
|
405 if (!(statp = getstat(s))) |
|
406 return 0; |
|
407 return statp->st_mode; |
|
408 } |
|
409 |
|
410 |
|
411 /* pem@aaii.oz; needed since dostat now uses "stat" */ |
|
412 |
|
413 /**/ |
|
414 static mode_t |
|
415 dolstat(char *s) |
|
416 { |
|
417 if (lstat(unmeta(s), &st) < 0) |
|
418 return 0; |
|
419 return st.st_mode; |
|
420 } |
|
421 |
|
422 |
|
423 /* |
|
424 * optison returns evalcond-friendly statuses (true, false, error). |
|
425 */ |
|
426 |
|
427 /**/ |
|
428 static int |
|
429 optison(char *name, char *s) |
|
430 { |
|
431 int i; |
|
432 |
|
433 if (strlen(s) == 1) |
|
434 i = optlookupc(*s); |
|
435 else |
|
436 i = optlookup(s); |
|
437 if (!i) { |
|
438 zwarnnam(name, "no such option: %s", s, 0); |
|
439 return 2; |
|
440 } else if(i < 0) |
|
441 return !unset(-i); |
|
442 else |
|
443 return !isset(i); |
|
444 } |
|
445 |
|
446 /**/ |
|
447 mod_export char * |
|
448 cond_str(char **args, int num, int raw) |
|
449 { |
|
450 char *s = args[num]; |
|
451 |
|
452 if (has_token(s)) { |
|
453 singsub(&s); |
|
454 if (!raw) |
|
455 untokenize(s); |
|
456 } |
|
457 return s; |
|
458 } |
|
459 |
|
460 /**/ |
|
461 mod_export zlong |
|
462 cond_val(char **args, int num) |
|
463 { |
|
464 char *s = args[num]; |
|
465 |
|
466 if (has_token(s)) { |
|
467 singsub(&s); |
|
468 untokenize(s); |
|
469 } |
|
470 return mathevali(s); |
|
471 } |
|
472 |
|
473 /**/ |
|
474 mod_export int |
|
475 cond_match(char **args, int num, char *str) |
|
476 { |
|
477 char *s = args[num]; |
|
478 |
|
479 singsub(&s); |
|
480 |
|
481 return matchpat(str, s); |
|
482 } |
|
483 |
|
484 /**/ |
|
485 static void |
|
486 tracemodcond(char *name, char **args, int inf) |
|
487 { |
|
488 char **aptr; |
|
489 |
|
490 args = arrdup(args); |
|
491 for (aptr = args; *aptr; aptr++) |
|
492 untokenize(*aptr); |
|
493 if (inf) { |
|
494 fprintf(xtrerr, " %s %s %s", args[0], name, args[1]); |
|
495 } else { |
|
496 fprintf(xtrerr, " %s", name); |
|
497 while (*args) |
|
498 fprintf(xtrerr, " %s", *args++); |
|
499 } |
|
500 } |