]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | /* GNU test program (ksb and mjb) */ |
2 | ||
3 | /* Modified to run with the GNU shell Apr 25, 1988 by bfox. */ | |
4 | ||
5 | /* Copyright (C) 1987, 1988, 1989, 1990, 1991 Free Software Foundation, Inc. | |
6 | ||
7 | This file is part of GNU Bash, the Bourne Again SHell. | |
8 | ||
9 | Bash is free software; you can redistribute it and/or modify it under | |
10 | the terms of the GNU General Public License as published by the Free | |
11 | Software Foundation; either version 2, or (at your option) any later | |
12 | version. | |
13 | ||
14 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY | |
15 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
17 | for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License along | |
20 | with Bash; see the file COPYING. If not, write to the Free Software | |
21 | Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
22 | ||
ccc6cda3 | 23 | /* Define STANDALONE to get the /bin/test version. Otherwise, you get |
726f6388 JA |
24 | the shell builtin version. */ |
25 | /* #define STANDALONE */ | |
26 | ||
ccc6cda3 JA |
27 | /* Define PATTERN_MATCHING to get the csh-like =~ and !~ pattern-matching |
28 | binary operators. */ | |
29 | /* #define PATTERN_MATCHING */ | |
30 | ||
31 | #if defined (HAVE_CONFIG_H) | |
32 | # include <config.h> | |
33 | #endif | |
34 | ||
726f6388 | 35 | #include <stdio.h> |
ccc6cda3 JA |
36 | |
37 | #if defined (STANDALONE) | |
38 | # include <sys/types.h> | |
39 | #else | |
40 | # include "bashtypes.h" | |
41 | #endif | |
42 | ||
43 | #if defined (HAVE_LIMITS_H) | |
44 | # include <limits.h> | |
45 | #else | |
46 | # include <sys/param.h> | |
47 | #endif | |
48 | ||
49 | #if defined (HAVE_UNISTD_H) | |
50 | # include <unistd.h> | |
51 | #endif | |
52 | ||
53 | #if !defined (_POSIX_VERSION) | |
54 | # include <sys/file.h> | |
55 | #endif /* !_POSIX_VERSION */ | |
56 | #include "posixstat.h" | |
57 | #include "filecntl.h" | |
726f6388 JA |
58 | |
59 | #if !defined (STANDALONE) | |
726f6388 | 60 | # include "shell.h" |
ccc6cda3 JA |
61 | # include "builtins/common.h" |
62 | # define main test_command | |
63 | # define isint legal_number | |
64 | # define getuid() current_user.uid | |
65 | # define geteuid() current_user.euid | |
66 | # define getgid() current_user.gid | |
67 | # define getegid() current_user.egid | |
726f6388 | 68 | #else /* STANDALONE */ |
726f6388 JA |
69 | # if !defined (S_IXUGO) |
70 | # define S_IXUGO 0111 | |
71 | # endif | |
72 | # if defined (HAVE_UNISTD_H) | |
73 | # include <unistd.h> | |
74 | # endif /* HAVE_UNISTD_H */ | |
75 | # define whitespace(c) (((c) == ' ') || ((c) == '\t')) | |
76 | # define digit(c) ((c) >= '0' && (c) <= '9') | |
77 | # define digit_value(c) ((c) - '0') | |
78 | #endif /* STANDALONE */ | |
79 | ||
80 | #if !defined (STRLEN) | |
81 | # define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0) | |
82 | #endif | |
83 | ||
84 | #include <errno.h> | |
85 | #if !defined (errno) | |
86 | extern int errno; | |
87 | #endif /* !errno */ | |
88 | ||
89 | #if !defined (STREQ) | |
90 | # define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0) | |
91 | #endif /* !STREQ */ | |
92 | ||
93 | #if !defined (member) | |
94 | # define member(c, s) (int)((c) ? (char *)strchr ((s), (c)) : 0) | |
95 | #endif /* !member */ | |
96 | ||
97 | /* Make gid_t and uid_t mean something for non-posix systems. */ | |
ccc6cda3 | 98 | #if defined (STANDALONE) && !defined (_POSIX_VERSION) && !defined (HAVE_UID_T) |
726f6388 JA |
99 | # if !defined (gid_t) |
100 | # define gid_t int | |
101 | # endif | |
102 | # if !defined (uid_t) | |
103 | # define uid_t int | |
104 | # endif | |
ccc6cda3 | 105 | #endif /* STANDALONE && !_POSIX_VERSION && !HAVE_UID_T */ |
726f6388 JA |
106 | |
107 | #if !defined (R_OK) | |
108 | #define R_OK 4 | |
109 | #define W_OK 2 | |
110 | #define X_OK 1 | |
111 | #define F_OK 0 | |
112 | #endif /* R_OK */ | |
113 | ||
ccc6cda3 JA |
114 | #define EQ 0 |
115 | #define NE 1 | |
116 | #define LT 2 | |
117 | #define GT 3 | |
118 | #define LE 4 | |
119 | #define GE 5 | |
120 | ||
121 | #define NT 0 | |
122 | #define OT 1 | |
123 | #define EF 2 | |
124 | ||
726f6388 JA |
125 | /* The following few defines control the truth and false output of each stage. |
126 | TRUE and FALSE are what we use to compute the final output value. | |
127 | SHELL_BOOLEAN is the form which returns truth or falseness in shell terms. | |
ccc6cda3 | 128 | Default is TRUE = 1, FALSE = 0, SHELL_BOOLEAN = (!value). */ |
726f6388 JA |
129 | #define TRUE 1 |
130 | #define FALSE 0 | |
131 | #define SHELL_BOOLEAN(value) (!(value)) | |
726f6388 JA |
132 | |
133 | #if defined (STANDALONE) | |
134 | # define test_exit(val) exit (val) | |
135 | #else | |
ccc6cda3 JA |
136 | static procenv_t test_exit_buf; |
137 | static int test_error_return; | |
726f6388 JA |
138 | # define test_exit(val) \ |
139 | do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0) | |
140 | #endif /* STANDALONE */ | |
141 | ||
142 | #if defined (AFS) | |
143 | /* We have to use access(2) for machines running AFS, because it's | |
144 | not a Unix file system. This may produce incorrect answers for | |
145 | non-AFS files. I hate AFS. */ | |
146 | # define EACCESS(path, mode) access(path, mode) | |
147 | #else | |
ccc6cda3 | 148 | # define EACCESS(path, mode) test_eaccess(path, mode) |
726f6388 JA |
149 | #endif /* AFS */ |
150 | ||
151 | static int pos; /* The offset of the current argument in ARGV. */ | |
152 | static int argc; /* The number of arguments present in ARGV. */ | |
153 | static char **argv; /* The argument list. */ | |
154 | static int noeval; | |
155 | ||
ccc6cda3 | 156 | #if defined (STANDALONE) |
726f6388 | 157 | static int isint (); |
ccc6cda3 | 158 | #endif |
726f6388 JA |
159 | static int unop (); |
160 | static int binop (); | |
161 | static int unary_operator (); | |
162 | static int binary_operator (); | |
163 | static int two_arguments (); | |
164 | static int three_arguments (); | |
165 | static int posixtest (); | |
166 | ||
167 | static int expr (); | |
168 | static int term (); | |
169 | static int and (); | |
170 | static int or (); | |
171 | ||
ccc6cda3 JA |
172 | static void beyond (); |
173 | ||
726f6388 JA |
174 | static void |
175 | test_syntax_error (format, arg) | |
176 | char *format, *arg; | |
177 | { | |
178 | #if !defined (STANDALONE) | |
179 | extern int interactive_shell; | |
180 | extern char *get_name_for_error (); | |
ccc6cda3 | 181 | if (interactive_shell == 0) |
726f6388 JA |
182 | fprintf (stderr, "%s: ", get_name_for_error ()); |
183 | #endif | |
184 | fprintf (stderr, "%s: ", argv[0]); | |
185 | fprintf (stderr, format, arg); | |
ccc6cda3 | 186 | fprintf (stderr, "\n"); |
726f6388 JA |
187 | fflush (stderr); |
188 | test_exit (SHELL_BOOLEAN (FALSE)); | |
189 | } | |
190 | ||
191 | /* A wrapper for stat () which disallows pathnames that are empty strings | |
192 | and handles /dev/fd emulation on systems that don't have it. */ | |
193 | static int | |
194 | test_stat (path, finfo) | |
195 | char *path; | |
196 | struct stat *finfo; | |
197 | { | |
198 | if (*path == '\0') | |
199 | { | |
200 | errno = ENOENT; | |
201 | return (-1); | |
202 | } | |
726f6388 JA |
203 | if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0) |
204 | { | |
ccc6cda3 JA |
205 | #if !defined (HAVE_DEV_FD) |
206 | long fd; | |
726f6388 | 207 | if (isint (path + 8, &fd)) |
ccc6cda3 | 208 | return (fstat ((int)fd, finfo)); |
726f6388 JA |
209 | else |
210 | { | |
211 | errno = EBADF; | |
212 | return (-1); | |
213 | } | |
ccc6cda3 JA |
214 | #else |
215 | /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a | |
216 | trailing slash. Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx. | |
217 | On most systems, with the notable exception of linux, this is | |
218 | effectively a no-op. */ | |
219 | char pbuf[32]; | |
220 | strcpy (pbuf, DEV_FD_PREFIX); | |
221 | strcat (pbuf, path + 8); | |
222 | return (stat (pbuf, finfo)); | |
726f6388 | 223 | #endif /* !HAVE_DEV_FD */ |
ccc6cda3 | 224 | } |
726f6388 JA |
225 | return (stat (path, finfo)); |
226 | } | |
227 | ||
228 | /* Do the same thing access(2) does, but use the effective uid and gid, | |
229 | and don't make the mistake of telling root that any file is | |
230 | executable. */ | |
231 | static int | |
ccc6cda3 | 232 | test_eaccess (path, mode) |
726f6388 JA |
233 | char *path; |
234 | int mode; | |
235 | { | |
236 | struct stat st; | |
237 | static int euid = -1; | |
238 | ||
239 | if (test_stat (path, &st) < 0) | |
240 | return (-1); | |
241 | ||
242 | if (euid == -1) | |
726f6388 | 243 | euid = geteuid (); |
726f6388 JA |
244 | |
245 | if (euid == 0) | |
246 | { | |
247 | /* Root can read or write any file. */ | |
248 | if (mode != X_OK) | |
249 | return (0); | |
250 | ||
251 | /* Root can execute any file that has any one of the execute | |
252 | bits set. */ | |
253 | if (st.st_mode & S_IXUGO) | |
254 | return (0); | |
255 | } | |
256 | ||
257 | if (st.st_uid == euid) /* owner */ | |
258 | mode <<= 6; | |
259 | else if (group_member (st.st_gid)) | |
260 | mode <<= 3; | |
261 | ||
262 | if (st.st_mode & mode) | |
263 | return (0); | |
264 | ||
265 | return (-1); | |
266 | } | |
267 | ||
268 | #if defined (HAVE_GETGROUPS) | |
269 | /* The number of groups that this user is a member of. */ | |
ccc6cda3 JA |
270 | static int ngroups, maxgroups; |
271 | static GETGROUPS_T *group_array = (GETGROUPS_T *)NULL; | |
726f6388 JA |
272 | #endif /* HAVE_GETGROUPS */ |
273 | ||
274 | #if !defined (NOGROUP) | |
ccc6cda3 | 275 | # define NOGROUP (gid_t) -1 |
726f6388 JA |
276 | #endif |
277 | ||
ccc6cda3 JA |
278 | #if defined (HAVE_GETGROUPS) |
279 | ||
280 | # if defined (NGROUPS_MAX) | |
281 | # define getmaxgroups() NGROUPS_MAX | |
282 | # else /* !NGROUPS_MAX */ | |
283 | # if defined (NGROUPS) | |
284 | # define getmaxgroups() NGROUPS | |
285 | # else /* !NGROUPS */ | |
286 | # define getmaxgroups() 64 | |
287 | # endif /* !NGROUPS */ | |
288 | # endif /* !NGROUPS_MAX */ | |
289 | ||
290 | #endif /* HAVE_GETGROUPS */ | |
291 | ||
726f6388 JA |
292 | /* Return non-zero if GID is one that we have in our groups list. */ |
293 | int | |
294 | group_member (gid) | |
ccc6cda3 | 295 | gid_t gid; |
726f6388 | 296 | { |
ccc6cda3 JA |
297 | static gid_t pgid = (gid_t)NOGROUP; |
298 | static gid_t egid = (gid_t)NOGROUP; | |
299 | #if defined (HAVE_GETGROUPS) | |
300 | register int i; | |
301 | #endif | |
302 | ||
303 | if (pgid == (gid_t)NOGROUP) | |
304 | pgid = (gid_t) getgid (); | |
305 | ||
306 | if (egid == (gid_t)NOGROUP) | |
307 | egid = (gid_t) getegid (); | |
726f6388 JA |
308 | |
309 | if (gid == pgid || gid == egid) | |
310 | return (1); | |
311 | ||
312 | #if defined (HAVE_GETGROUPS) | |
313 | /* getgroups () returns the number of elements that it was able to | |
ccc6cda3 JA |
314 | place into the array. */ |
315 | if (ngroups == 0) | |
726f6388 | 316 | { |
ccc6cda3 JA |
317 | if (maxgroups == 0) |
318 | maxgroups = getmaxgroups (); | |
319 | group_array = (GETGROUPS_T *)xrealloc (group_array, maxgroups * sizeof (GETGROUPS_T)); | |
320 | ngroups = getgroups (maxgroups, group_array); | |
726f6388 JA |
321 | } |
322 | ||
323 | /* In case of error, the user loses. */ | |
ccc6cda3 | 324 | if (ngroups <= 0) |
726f6388 JA |
325 | return (0); |
326 | ||
327 | /* Search through the list looking for GID. */ | |
ccc6cda3 JA |
328 | for (i = 0; i < ngroups; i++) |
329 | if (gid == (gid_t)group_array[i]) | |
330 | return (1); | |
726f6388 JA |
331 | #endif /* HAVE_GETGROUPS */ |
332 | ||
333 | return (0); | |
334 | } | |
335 | ||
336 | /* Increment our position in the argument list. Check that we're not | |
337 | past the end of the argument list. This check is supressed if the | |
338 | argument is FALSE. Made a macro for efficiency. */ | |
726f6388 | 339 | #define advance(f) do { ++pos; if (f && pos >= argc) beyond (); } while (0) |
726f6388 JA |
340 | #define unary_advance() do { advance (1); ++pos; } while (0) |
341 | ||
342 | /* | |
343 | * beyond - call when we're beyond the end of the argument list (an | |
344 | * error condition) | |
345 | */ | |
ccc6cda3 | 346 | static void |
726f6388 JA |
347 | beyond () |
348 | { | |
ccc6cda3 | 349 | test_syntax_error ("argument expected", (char *)NULL); |
726f6388 JA |
350 | } |
351 | ||
352 | /* Syntax error for when an integer argument was expected, but | |
353 | something else was found. */ | |
354 | static void | |
355 | integer_expected_error (pch) | |
356 | char *pch; | |
357 | { | |
ccc6cda3 | 358 | test_syntax_error ("%s: integer expression expected", pch); |
726f6388 JA |
359 | } |
360 | ||
ccc6cda3 | 361 | #if defined (STANDALONE) |
726f6388 JA |
362 | /* Return non-zero if the characters pointed to by STRING constitute a |
363 | valid number. Stuff the converted number into RESULT if RESULT is | |
364 | a non-null pointer to a long. */ | |
365 | static int | |
366 | isint (string, result) | |
367 | register char *string; | |
368 | long *result; | |
369 | { | |
370 | int sign; | |
371 | long value; | |
372 | ||
373 | sign = 1; | |
374 | value = 0; | |
375 | ||
376 | if (result) | |
377 | *result = 0; | |
378 | ||
379 | /* Skip leading whitespace characters. */ | |
380 | while (whitespace (*string)) | |
381 | string++; | |
382 | ||
383 | if (!*string) | |
384 | return (0); | |
385 | ||
386 | /* We allow leading `-' or `+'. */ | |
387 | if (*string == '-' || *string == '+') | |
388 | { | |
389 | if (!digit (string[1])) | |
390 | return (0); | |
391 | ||
392 | if (*string == '-') | |
393 | sign = -1; | |
394 | ||
395 | string++; | |
396 | } | |
397 | ||
398 | while (digit (*string)) | |
399 | { | |
400 | if (result) | |
401 | value = (value * 10) + digit_value (*string); | |
402 | string++; | |
403 | } | |
404 | ||
405 | /* Skip trailing whitespace, if any. */ | |
406 | while (whitespace (*string)) | |
407 | string++; | |
408 | ||
409 | /* Error if not at end of string. */ | |
410 | if (*string) | |
411 | return (0); | |
412 | ||
413 | if (result) | |
414 | { | |
415 | value *= sign; | |
416 | *result = value; | |
417 | } | |
418 | ||
419 | return (1); | |
420 | } | |
ccc6cda3 | 421 | #endif /* STANDALONE */ |
726f6388 JA |
422 | |
423 | /* | |
424 | * term - parse a term and return 1 or 0 depending on whether the term | |
425 | * evaluates to true or false, respectively. | |
426 | * | |
427 | * term ::= | |
ccc6cda3 JA |
428 | * '-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'p'|'r'|'s'|'u'|'w'|'x') filename |
429 | * '-'('G'|'L'|'O'|'S') filename | |
430 | * '-t' [int] | |
726f6388 JA |
431 | * '-'('z'|'n') string |
432 | * string | |
ccc6cda3 | 433 | * string ('!='|'='|'==') string |
726f6388 JA |
434 | * <int> '-'(eq|ne|le|lt|ge|gt) <int> |
435 | * file '-'(nt|ot|ef) file | |
436 | * '(' <expr> ')' | |
437 | * int ::= | |
726f6388 JA |
438 | * positive and negative integers |
439 | */ | |
440 | static int | |
441 | term () | |
442 | { | |
443 | int value; | |
444 | ||
445 | if (pos >= argc) | |
446 | beyond (); | |
447 | ||
ccc6cda3 JA |
448 | /* Deal with leading `not's. */ |
449 | if (argv[pos][0] == '!' && argv[pos][1] == '\0') | |
726f6388 | 450 | { |
ccc6cda3 JA |
451 | value = 0; |
452 | while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0') | |
726f6388 JA |
453 | { |
454 | advance (1); | |
ccc6cda3 | 455 | value = 1 - value; |
726f6388 JA |
456 | } |
457 | ||
ccc6cda3 | 458 | return (value ? !term() : term()); |
726f6388 JA |
459 | } |
460 | ||
ccc6cda3 JA |
461 | /* A paren-bracketed argument. */ |
462 | if (argv[pos][0] == '(' && argv[pos][1] == '\0') | |
726f6388 JA |
463 | { |
464 | advance (1); | |
465 | value = expr (); | |
466 | if (argv[pos] == 0) | |
ccc6cda3 | 467 | test_syntax_error ("`)' expected", (char *)NULL); |
726f6388 | 468 | else if (argv[pos][0] != ')' || argv[pos][1]) |
ccc6cda3 | 469 | test_syntax_error ("`)' expected, found %s", argv[pos]); |
726f6388 | 470 | advance (0); |
ccc6cda3 | 471 | return (value); |
726f6388 JA |
472 | } |
473 | ||
474 | /* are there enough arguments left that this could be dyadic? */ | |
ccc6cda3 | 475 | if ((pos + 3 <= argc) && binop (argv[pos + 1])) |
726f6388 JA |
476 | value = binary_operator (); |
477 | ||
478 | /* Might be a switch type argument */ | |
ccc6cda3 | 479 | else if (argv[pos][0] == '-' && argv[pos][2] == '\0') |
726f6388 JA |
480 | { |
481 | if (unop (argv[pos][1])) | |
482 | value = unary_operator (); | |
483 | else | |
ccc6cda3 | 484 | test_syntax_error ("%s: unary operator expected", argv[pos]); |
726f6388 JA |
485 | } |
486 | else | |
487 | { | |
ccc6cda3 | 488 | value = argv[pos][0] != '\0'; |
726f6388 JA |
489 | advance (0); |
490 | } | |
491 | ||
492 | return (value); | |
493 | } | |
494 | ||
495 | static int | |
ccc6cda3 JA |
496 | filecomp (s, t, op) |
497 | char *s, *t; | |
498 | int op; | |
726f6388 | 499 | { |
ccc6cda3 | 500 | struct stat st1, st2; |
726f6388 | 501 | |
ccc6cda3 JA |
502 | if (test_stat (s, &st1) < 0 || test_stat (t, &st2) < 0) |
503 | return (FALSE); | |
504 | switch (op) | |
726f6388 | 505 | { |
ccc6cda3 JA |
506 | case OT: return (st1.st_mtime < st2.st_mtime); |
507 | case NT: return (st1.st_mtime > st2.st_mtime); | |
508 | case EF: return ((st1.st_dev == st2.st_dev) && (st1.st_ino == st2.st_ino)); | |
509 | } | |
510 | return (FALSE); | |
511 | } | |
726f6388 | 512 | |
ccc6cda3 JA |
513 | static int |
514 | arithcomp (s, t, op) | |
515 | char *s, *t; | |
516 | int op; | |
517 | { | |
518 | long l, r; | |
726f6388 | 519 | |
ccc6cda3 JA |
520 | if (isint (s, &l) == 0) |
521 | integer_expected_error (s); | |
522 | if (isint (t, &r) == 0) | |
523 | integer_expected_error (t); | |
524 | switch (op) | |
525 | { | |
526 | case EQ: return (l == r); | |
527 | case NE: return (l != r); | |
528 | case LT: return (l < r); | |
529 | case GT: return (l > r); | |
530 | case LE: return (l <= r); | |
531 | case GE: return (l >= r); | |
726f6388 | 532 | } |
ccc6cda3 JA |
533 | return (FALSE); |
534 | } | |
535 | ||
536 | #if defined (PATTERN_MATCHING) | |
537 | static int | |
538 | patcomp (string, pat, op) | |
539 | char *string, *pat; | |
540 | int op; | |
541 | { | |
542 | int m; | |
543 | ||
544 | m = fnmatch (pat, string, 0); | |
545 | switch (op) | |
726f6388 | 546 | { |
ccc6cda3 JA |
547 | case EQ: return (m == 0); |
548 | case NE: return (m != 0); | |
726f6388 | 549 | } |
ccc6cda3 JA |
550 | } |
551 | #endif /* PATTERN_MATCHING */ | |
726f6388 | 552 | |
ccc6cda3 JA |
553 | static int |
554 | binary_operator () | |
555 | { | |
556 | int value; | |
557 | char *w; | |
558 | ||
559 | w = argv[pos + 1]; | |
560 | if (w[0] == '=' && (w[1] == '\0' || (w[1] == '=' && w[2] == '\0'))) | |
726f6388 | 561 | { |
ccc6cda3 JA |
562 | value = STREQ (argv[pos], argv[pos + 2]); |
563 | pos += 3; | |
564 | return (value); | |
565 | } | |
566 | if ((w[0] == '>' || w[0] == '<') && w[1] == '\0') | |
567 | { | |
568 | value = (w[0] == '>') ? strcmp (argv[pos], argv[pos + 2]) > 0 | |
569 | : strcmp (argv[pos], argv[pos + 2]) < 0; | |
570 | pos += 3; | |
571 | return (value); | |
572 | } | |
573 | #if defined (PATTERN_MATCHING) | |
574 | if ((w[0] == '=' || w[0] == '!') && w[1] == '~' && w[2] == '\0') | |
575 | { | |
576 | value = patcomp (argv[pos], argv[pos + 2], w[0] == '=' ? EQ : NE); | |
577 | pos += 3; | |
578 | return (value); | |
579 | } | |
580 | #endif | |
581 | if (w[0] == '!' && w[1] == '=' && w[2] == '\0') | |
582 | { | |
583 | value = STREQ (argv[pos], argv[pos + 2]) == 0; | |
584 | pos += 3; | |
585 | return (value); | |
586 | } | |
587 | ||
588 | if (w[0] != '-' || w[3] != '\0') | |
589 | { | |
590 | test_syntax_error ("%s: binary operator expected", w); | |
591 | /* NOTREACHED */ | |
592 | return (FALSE); | |
726f6388 | 593 | } |
726f6388 | 594 | |
ccc6cda3 JA |
595 | w++; |
596 | if (w[1] == 't') | |
726f6388 | 597 | { |
ccc6cda3 | 598 | switch (w[0]) |
726f6388 | 599 | { |
ccc6cda3 JA |
600 | case 'n': value = filecomp (argv[pos], argv[pos + 2], NT); break; |
601 | case 'o': value = filecomp (argv[pos], argv[pos + 2], OT); break; | |
602 | case 'l': value = arithcomp (argv[pos], argv[pos + 2], LT); break; | |
603 | case 'g': value = arithcomp (argv[pos], argv[pos + 2], GT); break; | |
604 | default: test_syntax_error ("-%s: binary operator expected", w); | |
726f6388 | 605 | } |
726f6388 | 606 | } |
ccc6cda3 | 607 | else if (w[0] == 'e') |
726f6388 | 608 | { |
ccc6cda3 JA |
609 | switch (w[1]) |
610 | { | |
611 | case 'q': value = arithcomp (argv[pos], argv[pos + 2], EQ); break; | |
612 | case 'f': value = filecomp (argv[pos], argv[pos + 2], EF); break; | |
613 | default: test_syntax_error ("-%s: binary operator expected", w); | |
614 | } | |
726f6388 | 615 | } |
ccc6cda3 | 616 | else if (w[1] == 'e') |
726f6388 | 617 | { |
ccc6cda3 JA |
618 | switch (w[0]) |
619 | { | |
620 | case 'n': value = arithcomp (argv[pos], argv[pos + 2], NE); break; | |
621 | case 'g': value = arithcomp (argv[pos], argv[pos + 2], GE); break; | |
622 | case 'l': value = arithcomp (argv[pos], argv[pos + 2], LE); break; | |
623 | default: test_syntax_error ("-%s: binary operator expected", w); | |
624 | } | |
726f6388 | 625 | } |
ccc6cda3 JA |
626 | else |
627 | test_syntax_error ("-%s: binary operator expected", w); | |
628 | ||
629 | pos += 3; | |
630 | return value; | |
726f6388 JA |
631 | } |
632 | ||
633 | static int | |
634 | unary_operator () | |
635 | { | |
ccc6cda3 | 636 | long r; |
726f6388 JA |
637 | struct stat stat_buf; |
638 | ||
639 | switch (argv[pos][1]) | |
640 | { | |
641 | default: | |
642 | return (FALSE); | |
643 | ||
644 | /* All of the following unary operators use unary_advance (), which | |
645 | checks to make sure that there is an argument, and then advances | |
646 | pos right past it. This means that pos - 1 is the location of the | |
647 | argument. */ | |
648 | ||
649 | case 'a': /* file exists in the file system? */ | |
650 | case 'e': | |
651 | unary_advance (); | |
ccc6cda3 | 652 | return (test_stat (argv[pos - 1], &stat_buf) == 0); |
726f6388 JA |
653 | |
654 | case 'r': /* file is readable? */ | |
655 | unary_advance (); | |
ccc6cda3 | 656 | return (EACCESS (argv[pos - 1], R_OK) == 0); |
726f6388 JA |
657 | |
658 | case 'w': /* File is writeable? */ | |
659 | unary_advance (); | |
ccc6cda3 | 660 | return (EACCESS (argv[pos - 1], W_OK) == 0); |
726f6388 JA |
661 | |
662 | case 'x': /* File is executable? */ | |
663 | unary_advance (); | |
ccc6cda3 | 664 | return (EACCESS (argv[pos - 1], X_OK) == 0); |
726f6388 JA |
665 | |
666 | case 'O': /* File is owned by you? */ | |
667 | unary_advance (); | |
ccc6cda3 JA |
668 | return (test_stat (argv[pos - 1], &stat_buf) == 0 && |
669 | (uid_t) geteuid () == (uid_t) stat_buf.st_uid); | |
726f6388 JA |
670 | |
671 | case 'G': /* File is owned by your group? */ | |
672 | unary_advance (); | |
ccc6cda3 JA |
673 | return (test_stat (argv[pos - 1], &stat_buf) == 0 && |
674 | (gid_t) getegid () == (gid_t) stat_buf.st_gid); | |
726f6388 JA |
675 | |
676 | case 'f': /* File is a file? */ | |
677 | unary_advance (); | |
678 | if (test_stat (argv[pos - 1], &stat_buf) < 0) | |
679 | return (FALSE); | |
680 | ||
ccc6cda3 | 681 | /* -f is true if the given file exists and is a regular file. */ |
726f6388 | 682 | #if defined (S_IFMT) |
ccc6cda3 | 683 | return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0); |
726f6388 | 684 | #else |
ccc6cda3 | 685 | return (S_ISREG (stat_buf.st_mode)); |
726f6388 JA |
686 | #endif /* !S_IFMT */ |
687 | ||
688 | case 'd': /* File is a directory? */ | |
689 | unary_advance (); | |
ccc6cda3 JA |
690 | return (test_stat (argv[pos - 1], &stat_buf) == 0 && |
691 | (S_ISDIR (stat_buf.st_mode))); | |
726f6388 JA |
692 | |
693 | case 's': /* File has something in it? */ | |
694 | unary_advance (); | |
ccc6cda3 JA |
695 | return (test_stat (argv[pos - 1], &stat_buf) == 0 && |
696 | stat_buf.st_size > (off_t) 0); | |
726f6388 JA |
697 | |
698 | case 'S': /* File is a socket? */ | |
699 | #if !defined (S_ISSOCK) | |
700 | return (FALSE); | |
701 | #else | |
702 | unary_advance (); | |
ccc6cda3 JA |
703 | return (test_stat (argv[pos - 1], &stat_buf) == 0 && |
704 | S_ISSOCK (stat_buf.st_mode)); | |
705 | #endif /* S_ISSOCK */ | |
726f6388 JA |
706 | |
707 | case 'c': /* File is character special? */ | |
708 | unary_advance (); | |
ccc6cda3 JA |
709 | return (test_stat (argv[pos - 1], &stat_buf) == 0 && |
710 | S_ISCHR (stat_buf.st_mode)); | |
726f6388 JA |
711 | |
712 | case 'b': /* File is block special? */ | |
713 | unary_advance (); | |
ccc6cda3 JA |
714 | return (test_stat (argv[pos - 1], &stat_buf) == 0 && |
715 | S_ISBLK (stat_buf.st_mode)); | |
726f6388 JA |
716 | |
717 | case 'p': /* File is a named pipe? */ | |
718 | unary_advance (); | |
719 | #ifndef S_ISFIFO | |
720 | return (FALSE); | |
721 | #else | |
ccc6cda3 JA |
722 | return (test_stat (argv[pos - 1], &stat_buf) == 0 && |
723 | S_ISFIFO (stat_buf.st_mode)); | |
724 | #endif /* S_ISFIFO */ | |
726f6388 JA |
725 | |
726 | case 'L': /* Same as -h */ | |
726f6388 JA |
727 | case 'h': /* File is a symbolic link? */ |
728 | unary_advance (); | |
ccc6cda3 | 729 | #if !defined (S_ISLNK) || !defined (HAVE_LSTAT) |
726f6388 JA |
730 | return (FALSE); |
731 | #else | |
ccc6cda3 JA |
732 | return ((argv[pos - 1][0] != '\0') && |
733 | (lstat (argv[pos - 1], &stat_buf) == 0) && | |
734 | S_ISLNK (stat_buf.st_mode)); | |
735 | #endif /* S_IFLNK && HAVE_LSTAT */ | |
726f6388 JA |
736 | |
737 | case 'u': /* File is setuid? */ | |
738 | unary_advance (); | |
ccc6cda3 JA |
739 | return (test_stat (argv[pos - 1], &stat_buf) == 0 && |
740 | (stat_buf.st_mode & S_ISUID) != 0); | |
726f6388 JA |
741 | |
742 | case 'g': /* File is setgid? */ | |
743 | unary_advance (); | |
ccc6cda3 JA |
744 | return (test_stat (argv[pos - 1], &stat_buf) == 0 && |
745 | (stat_buf.st_mode & S_ISGID) != 0); | |
726f6388 JA |
746 | |
747 | case 'k': /* File has sticky bit set? */ | |
748 | unary_advance (); | |
726f6388 JA |
749 | #if !defined (S_ISVTX) |
750 | /* This is not Posix, and is not defined on some Posix systems. */ | |
751 | return (FALSE); | |
752 | #else | |
ccc6cda3 JA |
753 | return (test_stat (argv[pos - 1], &stat_buf) == 0 && |
754 | (stat_buf.st_mode & S_ISVTX) != 0); | |
726f6388 JA |
755 | #endif |
756 | ||
ccc6cda3 | 757 | case 't': /* File fd is a terminal? fd defaults to stdout. */ |
726f6388 JA |
758 | advance (0); |
759 | if (pos < argc && isint (argv[pos], &r)) | |
760 | { | |
761 | advance (0); | |
ccc6cda3 | 762 | return (isatty ((int)r)); |
726f6388 | 763 | } |
ccc6cda3 | 764 | return (isatty (1)); |
726f6388 JA |
765 | |
766 | case 'n': /* True if arg has some length. */ | |
767 | unary_advance (); | |
ccc6cda3 | 768 | return (argv[pos - 1][0] != '\0'); |
726f6388 JA |
769 | |
770 | case 'z': /* True if arg has no length. */ | |
771 | unary_advance (); | |
ccc6cda3 JA |
772 | return (argv[pos - 1][0] == '\0'); |
773 | ||
774 | #if !defined (STANDALONE) | |
775 | case 'o': | |
776 | unary_advance (); | |
777 | return (minus_o_option_value (argv[pos - 1]) == 1); | |
778 | #endif /* !STANDALONE */ | |
726f6388 JA |
779 | } |
780 | } | |
ccc6cda3 | 781 | |
726f6388 JA |
782 | /* |
783 | * and: | |
784 | * term | |
785 | * term '-a' and | |
786 | */ | |
787 | static int | |
788 | and () | |
789 | { | |
ccc6cda3 | 790 | int value, v2; |
726f6388 JA |
791 | |
792 | value = term (); | |
793 | while (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2]) | |
794 | { | |
795 | advance (0); | |
ccc6cda3 JA |
796 | v2 = and (); |
797 | return (value && v2); | |
726f6388 | 798 | } |
ccc6cda3 | 799 | return (value); |
726f6388 JA |
800 | } |
801 | ||
802 | /* | |
803 | * or: | |
804 | * and | |
805 | * and '-o' or | |
806 | */ | |
807 | static int | |
808 | or () | |
809 | { | |
ccc6cda3 | 810 | int value, v2; |
726f6388 JA |
811 | |
812 | value = and (); | |
726f6388 JA |
813 | while (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2]) |
814 | { | |
815 | advance (0); | |
ccc6cda3 JA |
816 | v2 = or (); |
817 | return (value || v2); | |
726f6388 JA |
818 | } |
819 | ||
ccc6cda3 | 820 | return (value); |
726f6388 JA |
821 | } |
822 | ||
823 | /* | |
824 | * expr: | |
825 | * or | |
826 | */ | |
827 | static int | |
828 | expr () | |
829 | { | |
830 | if (pos >= argc) | |
831 | beyond (); | |
832 | ||
ccc6cda3 | 833 | return (FALSE ^ or ()); /* Same with this. */ |
726f6388 JA |
834 | } |
835 | ||
836 | /* Return TRUE if S is one of the test command's binary operators. */ | |
837 | static int | |
838 | binop (s) | |
839 | char *s; | |
840 | { | |
ccc6cda3 JA |
841 | char *t; |
842 | ||
843 | if (s[0] == '=' && s[1] == '\0') | |
844 | return (1); /* '=' */ | |
845 | else if (s[1] == '\0' && (s[0] == '<' || s[0] == '>')) /* string <, > */ | |
846 | return (1); | |
847 | else if (s[2] == '\0' && s[1] == '=' && (s[0] == '=' || s[0] == '!')) | |
848 | return (1); /* `==' and `!=' */ | |
849 | #if defined (PATTERN_MATCHING) | |
850 | else if (s[2] == '\0' && s[1] == '~' && (s[0] == '=' || s[0] == '!')) | |
851 | return (1); | |
852 | #endif | |
853 | else if (s[0] != '-' || s[2] == '\0' || s[3] != '\0') | |
854 | return (0); | |
855 | else | |
856 | { | |
857 | t = s + 1; | |
858 | if (t[1] == 't') | |
859 | switch (t[0]) | |
860 | { | |
861 | case 'n': /* -nt */ | |
862 | case 'o': /* -ot */ | |
863 | case 'l': /* -lt */ | |
864 | case 'g': /* -gt */ | |
865 | return (1); | |
866 | default: | |
867 | return (0); | |
868 | } | |
869 | else if (t[0] == 'e') | |
870 | switch (t[1]) | |
871 | { | |
872 | case 'q': /* -eq */ | |
873 | case 'f': /* -ef */ | |
874 | return (1); | |
875 | default: | |
876 | return (0); | |
877 | } | |
878 | else if (t[1] == 'e') | |
879 | switch (t[0]) | |
880 | { | |
881 | case 'n': /* -ne */ | |
882 | case 'l': /* -le */ | |
883 | case 'g': /* -ge */ | |
884 | return (1); | |
885 | default: | |
886 | return (0); | |
887 | } | |
888 | else | |
889 | return (0); | |
890 | } | |
726f6388 JA |
891 | } |
892 | ||
893 | /* Return non-zero if OP is one of the test command's unary operators. */ | |
894 | static int | |
895 | unop (op) | |
896 | int op; | |
897 | { | |
ccc6cda3 JA |
898 | switch (op) |
899 | { | |
900 | case 'a': case 'b': case 'c': case 'd': case 'e': | |
901 | case 'f': case 'g': case 'h': case 'k': case 'n': | |
902 | case 'p': case 'r': case 's': case 't': case 'u': | |
903 | case 'w': case 'x': case 'z': | |
904 | case 'G': case 'L': case 'O': case 'S': | |
905 | #if !defined (STANDALONE) | |
906 | case 'o': | |
907 | #endif | |
908 | return (1); | |
909 | } | |
910 | return (0); | |
726f6388 JA |
911 | } |
912 | ||
913 | static int | |
914 | two_arguments () | |
915 | { | |
ccc6cda3 JA |
916 | if (argv[pos][0] == '!' && argv[pos][1] == '\0') |
917 | return (argv[pos + 1][0] == '\0'); | |
918 | else if (argv[pos][0] == '-' && argv[pos][2] == '\0') | |
726f6388 JA |
919 | { |
920 | if (unop (argv[pos][1])) | |
ccc6cda3 | 921 | return (unary_operator ()); |
726f6388 | 922 | else |
ccc6cda3 | 923 | test_syntax_error ("%s: unary operator expected", argv[pos]); |
726f6388 JA |
924 | } |
925 | else | |
ccc6cda3 | 926 | test_syntax_error ("%s: unary operator expected", argv[pos]); |
726f6388 | 927 | |
ccc6cda3 | 928 | return (0); |
726f6388 JA |
929 | } |
930 | ||
ccc6cda3 JA |
931 | #define ANDOR(s) (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o')) |
932 | ||
933 | #define ONE_ARG_TEST(s) ((s)[0] != '\0') | |
934 | ||
726f6388 JA |
935 | static int |
936 | three_arguments () | |
937 | { | |
938 | int value; | |
939 | ||
ccc6cda3 JA |
940 | if (binop (argv[pos+1])) |
941 | { | |
942 | value = binary_operator (); | |
943 | pos = argc; | |
944 | } | |
945 | else if (ANDOR (argv[pos+1])) | |
946 | { | |
947 | if (argv[pos+1][1] == 'a') | |
948 | value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]); | |
949 | else | |
950 | value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]); | |
951 | pos = argc; | |
952 | } | |
953 | else if (argv[pos][0] == '!' && !argv[pos][1]) | |
726f6388 JA |
954 | { |
955 | advance (1); | |
956 | value = !two_arguments (); | |
957 | } | |
ccc6cda3 | 958 | else if (argv[pos][0] == '(' && argv[pos+2][0] == ')') |
726f6388 | 959 | { |
ccc6cda3 | 960 | value = ONE_ARG_TEST(argv[pos+1]); |
726f6388 JA |
961 | pos = argc; |
962 | } | |
726f6388 | 963 | else |
ccc6cda3 JA |
964 | test_syntax_error ("%s: binary operator expected", argv[pos+1]); |
965 | ||
726f6388 JA |
966 | return (value); |
967 | } | |
968 | ||
969 | /* This is an implementation of a Posix.2 proposal by David Korn. */ | |
970 | static int | |
971 | posixtest () | |
972 | { | |
973 | int value; | |
974 | ||
975 | switch (argc - 1) /* one extra passed in */ | |
976 | { | |
977 | case 0: | |
978 | value = FALSE; | |
979 | pos = argc; | |
980 | break; | |
981 | ||
982 | case 1: | |
ccc6cda3 | 983 | value = ONE_ARG_TEST(argv[1]); |
726f6388 JA |
984 | pos = argc; |
985 | break; | |
986 | ||
987 | case 2: | |
988 | value = two_arguments (); | |
989 | pos = argc; | |
990 | break; | |
991 | ||
992 | case 3: | |
993 | value = three_arguments (); | |
994 | break; | |
995 | ||
996 | case 4: | |
ccc6cda3 | 997 | if (argv[pos][0] == '!' && argv[pos][1] == '\0') |
726f6388 JA |
998 | { |
999 | advance (1); | |
1000 | value = !three_arguments (); | |
1001 | break; | |
1002 | } | |
1003 | /* FALLTHROUGH */ | |
726f6388 JA |
1004 | default: |
1005 | value = expr (); | |
1006 | } | |
1007 | ||
1008 | return (value); | |
1009 | } | |
1010 | ||
1011 | /* | |
1012 | * [: | |
1013 | * '[' expr ']' | |
1014 | * test: | |
1015 | * test expr | |
1016 | */ | |
1017 | int | |
726f6388 | 1018 | main (margc, margv) |
726f6388 JA |
1019 | int margc; |
1020 | char **margv; | |
1021 | { | |
1022 | int value; | |
1023 | ||
1024 | #if !defined (STANDALONE) | |
1025 | int code; | |
1026 | ||
1027 | code = setjmp (test_exit_buf); | |
1028 | ||
1029 | if (code) | |
1030 | return (test_error_return); | |
ccc6cda3 | 1031 | #endif /* !STANDALONE */ |
726f6388 JA |
1032 | |
1033 | argv = margv; | |
1034 | ||
ccc6cda3 | 1035 | if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0') |
726f6388 JA |
1036 | { |
1037 | --margc; | |
1038 | ||
1039 | if (margc < 2) | |
1040 | test_exit (SHELL_BOOLEAN (FALSE)); | |
1041 | ||
1042 | if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1])) | |
ccc6cda3 | 1043 | test_syntax_error ("missing `]'", (char *)NULL); |
726f6388 JA |
1044 | } |
1045 | ||
1046 | argc = margc; | |
1047 | pos = 1; | |
1048 | ||
1049 | if (pos >= argc) | |
1050 | test_exit (SHELL_BOOLEAN (FALSE)); | |
1051 | ||
1052 | noeval = 0; | |
1053 | value = posixtest (); | |
1054 | ||
1055 | if (pos != argc) | |
ccc6cda3 | 1056 | test_syntax_error ("too many arguments", (char *)NULL); |
726f6388 JA |
1057 | |
1058 | test_exit (SHELL_BOOLEAN (value)); | |
1059 | } |