1.5.1
by Matthias Klose
Import upstream version 4.3~rc1 |
1 |
/* test.c - 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-2010 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
|
|
10 |
it under the terms of the GNU General Public License as published by
|
|
11 |
the Free Software Foundation, either version 3 of the License, or
|
|
12 |
(at your option) any later version.
|
|
13 |
||
14 |
Bash is distributed in the hope that it will be useful,
|
|
15 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17 |
GNU General Public License for more details.
|
|
18 |
||
19 |
You should have received a copy of the GNU General Public License
|
|
20 |
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
|
21 |
*/
|
|
22 |
||
23 |
/* Define PATTERN_MATCHING to get the csh-like =~ and !~ pattern-matching
|
|
24 |
binary operators. */
|
|
25 |
/* #define PATTERN_MATCHING */
|
|
26 |
||
27 |
#if defined (HAVE_CONFIG_H)
|
|
28 |
# include <config.h>
|
|
29 |
#endif
|
|
30 |
||
31 |
#include <stdio.h> |
|
32 |
||
33 |
#include "bashtypes.h" |
|
34 |
||
35 |
#if !defined (HAVE_LIMITS_H) && defined (HAVE_SYS_PARAM_H)
|
|
36 |
# include <sys/param.h>
|
|
37 |
#endif
|
|
38 |
||
39 |
#if defined (HAVE_UNISTD_H)
|
|
40 |
# include <unistd.h>
|
|
41 |
#endif
|
|
42 |
||
43 |
#include <errno.h> |
|
44 |
#if !defined (errno)
|
|
45 |
extern int errno; |
|
46 |
#endif /* !errno */ |
|
47 |
||
48 |
#if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)
|
|
49 |
# include <sys/file.h>
|
|
50 |
#endif /* !_POSIX_VERSION */ |
|
51 |
#include "posixstat.h" |
|
52 |
#include "filecntl.h" |
|
53 |
#include "stat-time.h" |
|
54 |
||
55 |
#include "bashintl.h" |
|
56 |
||
57 |
#include "shell.h" |
|
58 |
#include "pathexp.h" |
|
59 |
#include "test.h" |
|
60 |
#include "builtins/common.h" |
|
61 |
||
62 |
#include <glob/strmatch.h> |
|
63 |
||
64 |
#if !defined (STRLEN)
|
|
65 |
# define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
|
|
66 |
#endif
|
|
67 |
||
68 |
#if !defined (STREQ)
|
|
69 |
# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0)
|
|
70 |
#endif /* !STREQ */ |
|
71 |
#define STRCOLLEQ(a, b) ((a)[0] == (b)[0] && strcoll ((a), (b)) == 0)
|
|
72 |
||
73 |
#if !defined (R_OK)
|
|
74 |
#define R_OK 4
|
|
75 |
#define W_OK 2
|
|
76 |
#define X_OK 1
|
|
77 |
#define F_OK 0
|
|
78 |
#endif /* R_OK */ |
|
79 |
||
80 |
#define EQ 0
|
|
81 |
#define NE 1
|
|
82 |
#define LT 2
|
|
83 |
#define GT 3
|
|
84 |
#define LE 4
|
|
85 |
#define GE 5
|
|
86 |
||
87 |
#define NT 0
|
|
88 |
#define OT 1
|
|
89 |
#define EF 2
|
|
90 |
||
91 |
/* The following few defines control the truth and false output of each stage.
|
|
92 |
TRUE and FALSE are what we use to compute the final output value.
|
|
93 |
SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
|
|
94 |
Default is TRUE = 1, FALSE = 0, SHELL_BOOLEAN = (!value). */
|
|
95 |
#define TRUE 1
|
|
96 |
#define FALSE 0
|
|
97 |
#define SHELL_BOOLEAN(value) (!(value))
|
|
98 |
||
99 |
#define TEST_ERREXIT_STATUS 2
|
|
100 |
||
101 |
static procenv_t test_exit_buf; |
|
102 |
static int test_error_return; |
|
103 |
#define test_exit(val) \
|
|
104 |
do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0)
|
|
105 |
||
106 |
extern int sh_stat __P((const char *, struct stat *)); |
|
107 |
||
108 |
static int pos; /* The offset of the current argument in ARGV. */ |
|
109 |
static int argc; /* The number of arguments present in ARGV. */ |
|
110 |
static char **argv; /* The argument list. */ |
|
111 |
static int noeval; |
|
112 |
||
113 |
static void test_syntax_error __P((char *, char *)) __attribute__((__noreturn__)); |
|
114 |
static void beyond __P((void)) __attribute__((__noreturn__)); |
|
115 |
static void integer_expected_error __P((char *)) __attribute__((__noreturn__)); |
|
116 |
||
117 |
static int unary_operator __P((void)); |
|
118 |
static int binary_operator __P((void)); |
|
119 |
static int two_arguments __P((void)); |
|
120 |
static int three_arguments __P((void)); |
|
121 |
static int posixtest __P((void)); |
|
122 |
||
123 |
static int expr __P((void)); |
|
124 |
static int term __P((void)); |
|
125 |
static int and __P((void)); |
|
126 |
static int or __P((void)); |
|
127 |
||
128 |
static int filecomp __P((char *, char *, int)); |
|
129 |
static int arithcomp __P((char *, char *, int, int)); |
|
130 |
static int patcomp __P((char *, char *, int)); |
|
131 |
||
132 |
static void |
|
133 |
test_syntax_error (format, arg) |
|
134 |
char *format, *arg; |
|
135 |
{
|
|
136 |
builtin_error (format, arg); |
|
137 |
test_exit (TEST_ERREXIT_STATUS); |
|
138 |
}
|
|
139 |
||
140 |
/*
|
|
141 |
* beyond - call when we're beyond the end of the argument list (an
|
|
142 |
* error condition)
|
|
143 |
*/
|
|
144 |
static void |
|
145 |
beyond () |
|
146 |
{
|
|
147 |
test_syntax_error (_("argument expected"), (char *)NULL); |
|
148 |
}
|
|
149 |
||
150 |
/* Syntax error for when an integer argument was expected, but
|
|
151 |
something else was found. */
|
|
152 |
static void |
|
153 |
integer_expected_error (pch) |
|
154 |
char *pch; |
|
155 |
{
|
|
156 |
test_syntax_error (_("%s: integer expression expected"), pch); |
|
157 |
}
|
|
158 |
||
159 |
/* Increment our position in the argument list. Check that we're not
|
|
160 |
past the end of the argument list. This check is suppressed if the
|
|
161 |
argument is FALSE. Made a macro for efficiency. */
|
|
162 |
#define advance(f) do { ++pos; if (f && pos >= argc) beyond (); } while (0)
|
|
163 |
#define unary_advance() do { advance (1); ++pos; } while (0)
|
|
164 |
||
165 |
/*
|
|
166 |
* expr:
|
|
167 |
* or
|
|
168 |
*/
|
|
169 |
static int |
|
170 |
expr () |
|
171 |
{
|
|
172 |
if (pos >= argc) |
|
173 |
beyond (); |
|
174 |
||
175 |
return (FALSE ^ or ()); /* Same with this. */ |
|
176 |
}
|
|
177 |
||
178 |
/*
|
|
179 |
* or:
|
|
180 |
* and
|
|
181 |
* and '-o' or
|
|
182 |
*/
|
|
183 |
static int |
|
184 |
or () |
|
185 |
{
|
|
186 |
int value, v2; |
|
187 |
||
188 |
value = and (); |
|
189 |
if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2]) |
|
190 |
{
|
|
191 |
advance (0); |
|
192 |
v2 = or (); |
|
193 |
return (value || v2); |
|
194 |
}
|
|
195 |
||
196 |
return (value); |
|
197 |
}
|
|
198 |
||
199 |
/*
|
|
200 |
* and:
|
|
201 |
* term
|
|
202 |
* term '-a' and
|
|
203 |
*/
|
|
204 |
static int |
|
205 |
and () |
|
206 |
{
|
|
207 |
int value, v2; |
|
208 |
||
209 |
value = term (); |
|
210 |
if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2]) |
|
211 |
{
|
|
212 |
advance (0); |
|
213 |
v2 = and (); |
|
214 |
return (value && v2); |
|
215 |
}
|
|
216 |
return (value); |
|
217 |
}
|
|
218 |
||
219 |
/*
|
|
220 |
* term - parse a term and return 1 or 0 depending on whether the term
|
|
221 |
* evaluates to true or false, respectively.
|
|
222 |
*
|
|
223 |
* term ::=
|
|
224 |
* '-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'k'|'p'|'r'|'s'|'u'|'w'|'x') filename
|
|
225 |
* '-'('G'|'L'|'O'|'S'|'N') filename
|
|
226 |
* '-t' [int]
|
|
227 |
* '-'('z'|'n') string
|
|
228 |
* '-o' option
|
|
229 |
* string
|
|
230 |
* string ('!='|'='|'==') string
|
|
231 |
* <int> '-'(eq|ne|le|lt|ge|gt) <int>
|
|
232 |
* file '-'(nt|ot|ef) file
|
|
233 |
* '(' <expr> ')'
|
|
234 |
* int ::=
|
|
235 |
* positive and negative integers
|
|
236 |
*/
|
|
237 |
static int |
|
238 |
term () |
|
239 |
{
|
|
240 |
int value; |
|
241 |
||
242 |
if (pos >= argc) |
|
243 |
beyond (); |
|
244 |
||
245 |
/* Deal with leading `not's. */
|
|
246 |
if (argv[pos][0] == '!' && argv[pos][1] == '\0') |
|
247 |
{
|
|
248 |
value = 0; |
|
249 |
while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0') |
|
250 |
{
|
|
251 |
advance (1); |
|
252 |
value = 1 - value; |
|
253 |
}
|
|
254 |
||
255 |
return (value ? !term() : term()); |
|
256 |
}
|
|
257 |
||
258 |
/* A paren-bracketed argument. */
|
|
259 |
if (argv[pos][0] == '(' && argv[pos][1] == '\0') /* ) */ |
|
260 |
{
|
|
261 |
advance (1); |
|
262 |
value = expr (); |
|
263 |
if (argv[pos] == 0) /* ( */ |
|
264 |
test_syntax_error (_("`)' expected"), (char *)NULL); |
|
265 |
else if (argv[pos][0] != ')' || argv[pos][1]) /* ( */ |
|
266 |
test_syntax_error (_("`)' expected, found %s"), argv[pos]); |
|
267 |
advance (0); |
|
268 |
return (value); |
|
269 |
}
|
|
270 |
||
271 |
/* are there enough arguments left that this could be dyadic? */
|
|
272 |
if ((pos + 3 <= argc) && test_binop (argv[pos + 1])) |
|
273 |
value = binary_operator (); |
|
274 |
||
275 |
/* Might be a switch type argument */
|
|
276 |
else if (argv[pos][0] == '-' && argv[pos][2] == '\0') |
|
277 |
{
|
|
278 |
if (test_unop (argv[pos])) |
|
279 |
value = unary_operator (); |
|
280 |
else
|
|
281 |
test_syntax_error (_("%s: unary operator expected"), argv[pos]); |
|
282 |
}
|
|
283 |
else
|
|
284 |
{
|
|
285 |
value = argv[pos][0] != '\0'; |
|
286 |
advance (0); |
|
287 |
}
|
|
288 |
||
289 |
return (value); |
|
290 |
}
|
|
291 |
||
292 |
static int |
|
293 |
stat_mtime (fn, st, ts) |
|
294 |
char *fn; |
|
295 |
struct stat *st; |
|
296 |
struct timespec *ts; |
|
297 |
{
|
|
298 |
int r; |
|
299 |
||
300 |
r = sh_stat (fn, st); |
|
301 |
if (r < 0) |
|
302 |
return r; |
|
303 |
*ts = get_stat_mtime (st); |
|
304 |
return 0; |
|
305 |
}
|
|
306 |
||
307 |
static int |
|
308 |
filecomp (s, t, op) |
|
309 |
char *s, *t; |
|
310 |
int op; |
|
311 |
{
|
|
312 |
struct stat st1, st2; |
|
313 |
struct timespec ts1, ts2; |
|
314 |
int r1, r2; |
|
315 |
||
316 |
if ((r1 = stat_mtime (s, &st1, &ts1)) < 0) |
|
317 |
{
|
|
318 |
if (op == EF) |
|
319 |
return (FALSE); |
|
320 |
}
|
|
321 |
if ((r2 = stat_mtime (t, &st2, &ts2)) < 0) |
|
322 |
{
|
|
323 |
if (op == EF) |
|
324 |
return (FALSE); |
|
325 |
}
|
|
326 |
||
327 |
switch (op) |
|
328 |
{
|
|
329 |
case OT: return (r1 < r2 || (r2 == 0 && timespec_cmp (ts1, ts2) < 0)); |
|
330 |
case NT: return (r1 > r2 || (r1 == 0 && timespec_cmp (ts1, ts2) > 0)); |
|
331 |
case EF: return (same_file (s, t, &st1, &st2)); |
|
332 |
}
|
|
333 |
return (FALSE); |
|
334 |
}
|
|
335 |
||
336 |
static int |
|
337 |
arithcomp (s, t, op, flags) |
|
338 |
char *s, *t; |
|
339 |
int op, flags; |
|
340 |
{
|
|
341 |
intmax_t l, r; |
|
342 |
int expok; |
|
343 |
||
344 |
if (flags & TEST_ARITHEXP) |
|
345 |
{
|
|
346 |
l = evalexp (s, &expok); |
|
347 |
if (expok == 0) |
|
348 |
return (FALSE); /* should probably longjmp here */ |
|
349 |
r = evalexp (t, &expok); |
|
350 |
if (expok == 0) |
|
351 |
return (FALSE); /* ditto */ |
|
352 |
}
|
|
353 |
else
|
|
354 |
{
|
|
355 |
if (legal_number (s, &l) == 0) |
|
356 |
integer_expected_error (s); |
|
357 |
if (legal_number (t, &r) == 0) |
|
358 |
integer_expected_error (t); |
|
359 |
}
|
|
360 |
||
361 |
switch (op) |
|
362 |
{
|
|
363 |
case EQ: return (l == r); |
|
364 |
case NE: return (l != r); |
|
365 |
case LT: return (l < r); |
|
366 |
case GT: return (l > r); |
|
367 |
case LE: return (l <= r); |
|
368 |
case GE: return (l >= r); |
|
369 |
}
|
|
370 |
||
371 |
return (FALSE); |
|
372 |
}
|
|
373 |
||
374 |
static int |
|
375 |
patcomp (string, pat, op) |
|
376 |
char *string, *pat; |
|
377 |
int op; |
|
378 |
{
|
|
379 |
int m; |
|
380 |
||
381 |
m = strmatch (pat, string, FNMATCH_EXTFLAG|FNMATCH_IGNCASE); |
|
382 |
return ((op == EQ) ? (m == 0) : (m != 0)); |
|
383 |
}
|
|
384 |
||
385 |
int
|
|
386 |
binary_test (op, arg1, arg2, flags) |
|
387 |
char *op, *arg1, *arg2; |
|
388 |
int flags; |
|
389 |
{
|
|
390 |
int patmatch; |
|
391 |
||
392 |
patmatch = (flags & TEST_PATMATCH); |
|
393 |
||
394 |
if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0'))) |
|
395 |
return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2)); |
|
396 |
else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0') |
|
397 |
{
|
|
398 |
#if defined (HAVE_STRCOLL)
|
|
399 |
if (shell_compatibility_level > 40 && flags & TEST_LOCALE) |
|
400 |
return ((op[0] == '>') ? (strcoll (arg1, arg2) > 0) : (strcoll (arg1, arg2) < 0)); |
|
401 |
else
|
|
402 |
#endif
|
|
403 |
return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0)); |
|
404 |
}
|
|
405 |
else if (op[0] == '!' && op[1] == '=' && op[2] == '\0') |
|
406 |
return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0)); |
|
407 |
||
408 |
||
409 |
else if (op[2] == 't') |
|
410 |
{
|
|
411 |
switch (op[1]) |
|
412 |
{
|
|
413 |
case 'n': return (filecomp (arg1, arg2, NT)); /* -nt */ |
|
414 |
case 'o': return (filecomp (arg1, arg2, OT)); /* -ot */ |
|
415 |
case 'l': return (arithcomp (arg1, arg2, LT, flags)); /* -lt */ |
|
416 |
case 'g': return (arithcomp (arg1, arg2, GT, flags)); /* -gt */ |
|
417 |
}
|
|
418 |
}
|
|
419 |
else if (op[1] == 'e') |
|
420 |
{
|
|
421 |
switch (op[2]) |
|
422 |
{
|
|
423 |
case 'f': return (filecomp (arg1, arg2, EF)); /* -ef */ |
|
424 |
case 'q': return (arithcomp (arg1, arg2, EQ, flags)); /* -eq */ |
|
425 |
}
|
|
426 |
}
|
|
427 |
else if (op[2] == 'e') |
|
428 |
{
|
|
429 |
switch (op[1]) |
|
430 |
{
|
|
431 |
case 'n': return (arithcomp (arg1, arg2, NE, flags)); /* -ne */ |
|
432 |
case 'g': return (arithcomp (arg1, arg2, GE, flags)); /* -ge */ |
|
433 |
case 'l': return (arithcomp (arg1, arg2, LE, flags)); /* -le */ |
|
434 |
}
|
|
435 |
}
|
|
436 |
||
437 |
return (FALSE); /* should never get here */ |
|
438 |
}
|
|
439 |
||
440 |
||
441 |
static int |
|
442 |
binary_operator () |
|
443 |
{
|
|
444 |
int value; |
|
445 |
char *w; |
|
446 |
||
447 |
w = argv[pos + 1]; |
|
448 |
if ((w[0] == '=' && (w[1] == '\0' || (w[1] == '=' && w[2] == '\0'))) || /* =, == */ |
|
449 |
((w[0] == '>' || w[0] == '<') && w[1] == '\0') || /* <, > */ |
|
450 |
(w[0] == '!' && w[1] == '=' && w[2] == '\0')) /* != */ |
|
451 |
{
|
|
452 |
value = binary_test (w, argv[pos], argv[pos + 2], 0); |
|
453 |
pos += 3; |
|
454 |
return (value); |
|
455 |
}
|
|
456 |
||
457 |
#if defined (PATTERN_MATCHING)
|
|
458 |
if ((w[0] == '=' || w[0] == '!') && w[1] == '~' && w[2] == '\0') |
|
459 |
{
|
|
460 |
value = patcomp (argv[pos], argv[pos + 2], w[0] == '=' ? EQ : NE); |
|
461 |
pos += 3; |
|
462 |
return (value); |
|
463 |
}
|
|
464 |
#endif
|
|
465 |
||
466 |
if ((w[0] != '-' || w[3] != '\0') || test_binop (w) == 0) |
|
467 |
{
|
|
468 |
test_syntax_error (_("%s: binary operator expected"), w); |
|
469 |
/* NOTREACHED */
|
|
470 |
return (FALSE); |
|
471 |
}
|
|
472 |
||
473 |
value = binary_test (w, argv[pos], argv[pos + 2], 0); |
|
474 |
pos += 3; |
|
475 |
return value; |
|
476 |
}
|
|
477 |
||
478 |
static int |
|
479 |
unary_operator () |
|
480 |
{
|
|
481 |
char *op; |
|
482 |
intmax_t r; |
|
483 |
||
484 |
op = argv[pos]; |
|
485 |
if (test_unop (op) == 0) |
|
486 |
return (FALSE); |
|
487 |
||
488 |
/* the only tricky case is `-t', which may or may not take an argument. */
|
|
489 |
if (op[1] == 't') |
|
490 |
{
|
|
491 |
advance (0); |
|
492 |
if (pos < argc) |
|
493 |
{
|
|
494 |
if (legal_number (argv[pos], &r)) |
|
495 |
{
|
|
496 |
advance (0); |
|
497 |
return (unary_test (op, argv[pos - 1])); |
|
498 |
}
|
|
499 |
else
|
|
500 |
return (FALSE); |
|
501 |
}
|
|
502 |
else
|
|
503 |
return (unary_test (op, "1")); |
|
504 |
}
|
|
505 |
||
506 |
/* All of the unary operators take an argument, so we first call
|
|
507 |
unary_advance (), which checks to make sure that there is an
|
|
508 |
argument, and then advances pos right past it. This means that
|
|
509 |
pos - 1 is the location of the argument. */
|
|
510 |
unary_advance (); |
|
511 |
return (unary_test (op, argv[pos - 1])); |
|
512 |
}
|
|
513 |
||
514 |
int
|
|
515 |
unary_test (op, arg) |
|
516 |
char *op, *arg; |
|
517 |
{
|
|
518 |
intmax_t r; |
|
519 |
struct stat stat_buf; |
|
520 |
SHELL_VAR *v; |
|
521 |
||
522 |
switch (op[1]) |
|
523 |
{
|
|
524 |
case 'a': /* file exists in the file system? */ |
|
525 |
case 'e': |
|
526 |
return (sh_stat (arg, &stat_buf) == 0); |
|
527 |
||
528 |
case 'r': /* file is readable? */ |
|
529 |
return (sh_eaccess (arg, R_OK) == 0); |
|
530 |
||
531 |
case 'w': /* File is writeable? */ |
|
532 |
return (sh_eaccess (arg, W_OK) == 0); |
|
533 |
||
534 |
case 'x': /* File is executable? */ |
|
535 |
return (sh_eaccess (arg, X_OK) == 0); |
|
536 |
||
537 |
case 'O': /* File is owned by you? */ |
|
538 |
return (sh_stat (arg, &stat_buf) == 0 && |
|
539 |
(uid_t) current_user.euid == (uid_t) stat_buf.st_uid); |
|
540 |
||
541 |
case 'G': /* File is owned by your group? */ |
|
542 |
return (sh_stat (arg, &stat_buf) == 0 && |
|
543 |
(gid_t) current_user.egid == (gid_t) stat_buf.st_gid); |
|
544 |
||
545 |
case 'N': |
|
546 |
return (sh_stat (arg, &stat_buf) == 0 && |
|
547 |
stat_buf.st_atime <= stat_buf.st_mtime); |
|
548 |
||
549 |
case 'f': /* File is a file? */ |
|
550 |
if (sh_stat (arg, &stat_buf) < 0) |
|
551 |
return (FALSE); |
|
552 |
||
553 |
/* -f is true if the given file exists and is a regular file. */
|
|
554 |
#if defined (S_IFMT)
|
|
555 |
return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0); |
|
556 |
#else
|
|
557 |
return (S_ISREG (stat_buf.st_mode)); |
|
558 |
#endif /* !S_IFMT */ |
|
559 |
||
560 |
case 'd': /* File is a directory? */ |
|
561 |
return (sh_stat (arg, &stat_buf) == 0 && (S_ISDIR (stat_buf.st_mode))); |
|
562 |
||
563 |
case 's': /* File has something in it? */ |
|
564 |
return (sh_stat (arg, &stat_buf) == 0 && stat_buf.st_size > (off_t) 0); |
|
565 |
||
566 |
case 'S': /* File is a socket? */ |
|
567 |
#if !defined (S_ISSOCK)
|
|
568 |
return (FALSE); |
|
569 |
#else
|
|
570 |
return (sh_stat (arg, &stat_buf) == 0 && S_ISSOCK (stat_buf.st_mode)); |
|
571 |
#endif /* S_ISSOCK */ |
|
572 |
||
573 |
case 'c': /* File is character special? */ |
|
574 |
return (sh_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode)); |
|
575 |
||
576 |
case 'b': /* File is block special? */ |
|
577 |
return (sh_stat (arg, &stat_buf) == 0 && S_ISBLK (stat_buf.st_mode)); |
|
578 |
||
579 |
case 'p': /* File is a named pipe? */ |
|
580 |
#ifndef S_ISFIFO
|
|
581 |
return (FALSE); |
|
582 |
#else
|
|
583 |
return (sh_stat (arg, &stat_buf) == 0 && S_ISFIFO (stat_buf.st_mode)); |
|
584 |
#endif /* S_ISFIFO */ |
|
585 |
||
586 |
case 'L': /* Same as -h */ |
|
587 |
case 'h': /* File is a symbolic link? */ |
|
588 |
#if !defined (S_ISLNK) || !defined (HAVE_LSTAT)
|
|
589 |
return (FALSE); |
|
590 |
#else
|
|
591 |
return ((arg[0] != '\0') && |
|
592 |
(lstat (arg, &stat_buf) == 0) && S_ISLNK (stat_buf.st_mode)); |
|
593 |
#endif /* S_IFLNK && HAVE_LSTAT */ |
|
594 |
||
595 |
case 'u': /* File is setuid? */ |
|
596 |
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0); |
|
597 |
||
598 |
case 'g': /* File is setgid? */ |
|
599 |
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISGID) != 0); |
|
600 |
||
601 |
case 'k': /* File has sticky bit set? */ |
|
602 |
#if !defined (S_ISVTX)
|
|
603 |
/* This is not Posix, and is not defined on some Posix systems. */
|
|
604 |
return (FALSE); |
|
605 |
#else
|
|
606 |
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISVTX) != 0); |
|
607 |
#endif
|
|
608 |
||
609 |
case 't': /* File fd is a terminal? */ |
|
610 |
if (legal_number (arg, &r) == 0) |
|
611 |
return (FALSE); |
|
612 |
return ((r == (int)r) && isatty ((int)r)); |
|
613 |
||
614 |
case 'n': /* True if arg has some length. */ |
|
615 |
return (arg[0] != '\0'); |
|
616 |
||
617 |
case 'z': /* True if arg has no length. */ |
|
618 |
return (arg[0] == '\0'); |
|
619 |
||
620 |
case 'o': /* True if option `arg' is set. */ |
|
621 |
return (minus_o_option_value (arg) == 1); |
|
622 |
||
623 |
case 'v': |
|
624 |
v = find_variable (arg); |
|
1.4.3
by Matthias Klose
Import upstream version 4.3 |
625 |
#if defined (ARRAY_VARS)
|
626 |
if (v == 0 && valid_array_reference (arg)) |
|
627 |
{
|
|
628 |
char *t; |
|
629 |
t = array_value (arg, 0, 0, (int *)0, (arrayind_t *)0); |
|
630 |
return (t ? TRUE : FALSE); |
|
631 |
}
|
|
632 |
else if (v && invisible_p (v) == 0 && array_p (v)) |
|
633 |
{
|
|
634 |
char *t; |
|
635 |
/* [[ -v foo ]] == [[ -v foo[0] ]] */
|
|
636 |
t = array_reference (array_cell (v), 0); |
|
637 |
return (t ? TRUE : FALSE); |
|
638 |
}
|
|
639 |
else if (v && invisible_p (v) == 0 && assoc_p (v)) |
|
640 |
{
|
|
641 |
char *t; |
|
642 |
t = assoc_reference (assoc_cell (v), "0"); |
|
643 |
return (t ? TRUE : FALSE); |
|
644 |
}
|
|
645 |
#endif
|
|
1.5.1
by Matthias Klose
Import upstream version 4.3~rc1 |
646 |
return (v && invisible_p (v) == 0 && var_isset (v) ? TRUE : FALSE); |
647 |
||
648 |
case 'R': |
|
2.1.25
by Matthias Klose
* Apply upstream patches 001 - 008: |
649 |
v = find_variable_noref (arg); |
650 |
return ((v && invisible_p (v) == 0 && var_isset (v) && nameref_p (v)) ? TRUE : FALSE); |
|
1.5.1
by Matthias Klose
Import upstream version 4.3~rc1 |
651 |
}
|
652 |
||
653 |
/* We can't actually get here, but this shuts up gcc. */
|
|
654 |
return (FALSE); |
|
655 |
}
|
|
656 |
||
657 |
/* Return TRUE if OP is one of the test command's binary operators. */
|
|
658 |
int
|
|
659 |
test_binop (op) |
|
660 |
char *op; |
|
661 |
{
|
|
662 |
if (op[0] == '=' && op[1] == '\0') |
|
663 |
return (1); /* '=' */ |
|
664 |
else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0') /* string <, > */ |
|
665 |
return (1); |
|
666 |
else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0') |
|
667 |
return (1); /* `==' and `!=' */ |
|
668 |
#if defined (PATTERN_MATCHING)
|
|
669 |
else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!')) |
|
670 |
return (1); |
|
671 |
#endif
|
|
672 |
else if (op[0] != '-' || op[2] == '\0' || op[3] != '\0') |
|
673 |
return (0); |
|
674 |
else
|
|
675 |
{
|
|
676 |
if (op[2] == 't') |
|
677 |
switch (op[1]) |
|
678 |
{
|
|
679 |
case 'n': /* -nt */ |
|
680 |
case 'o': /* -ot */ |
|
681 |
case 'l': /* -lt */ |
|
682 |
case 'g': /* -gt */ |
|
683 |
return (1); |
|
684 |
default: |
|
685 |
return (0); |
|
686 |
}
|
|
687 |
else if (op[1] == 'e') |
|
688 |
switch (op[2]) |
|
689 |
{
|
|
690 |
case 'q': /* -eq */ |
|
691 |
case 'f': /* -ef */ |
|
692 |
return (1); |
|
693 |
default: |
|
694 |
return (0); |
|
695 |
}
|
|
696 |
else if (op[2] == 'e') |
|
697 |
switch (op[1]) |
|
698 |
{
|
|
699 |
case 'n': /* -ne */ |
|
700 |
case 'g': /* -ge */ |
|
701 |
case 'l': /* -le */ |
|
702 |
return (1); |
|
703 |
default: |
|
704 |
return (0); |
|
705 |
}
|
|
706 |
else
|
|
707 |
return (0); |
|
708 |
}
|
|
709 |
}
|
|
710 |
||
711 |
/* Return non-zero if OP is one of the test command's unary operators. */
|
|
712 |
int
|
|
713 |
test_unop (op) |
|
714 |
char *op; |
|
715 |
{
|
|
716 |
if (op[0] != '-' || op[2] != 0) |
|
717 |
return (0); |
|
718 |
||
719 |
switch (op[1]) |
|
720 |
{
|
|
721 |
case 'a': case 'b': case 'c': case 'd': case 'e': |
|
722 |
case 'f': case 'g': case 'h': case 'k': case 'n': |
|
723 |
case 'o': case 'p': case 'r': case 's': case 't': |
|
724 |
case 'u': case 'v': case 'w': case 'x': case 'z': |
|
725 |
case 'G': case 'L': case 'O': case 'S': case 'N': |
|
2.1.25
by Matthias Klose
* Apply upstream patches 001 - 008: |
726 |
case 'R': |
1.5.1
by Matthias Klose
Import upstream version 4.3~rc1 |
727 |
return (1); |
728 |
}
|
|
729 |
||
730 |
return (0); |
|
731 |
}
|
|
732 |
||
733 |
static int |
|
734 |
two_arguments () |
|
735 |
{
|
|
736 |
if (argv[pos][0] == '!' && argv[pos][1] == '\0') |
|
737 |
return (argv[pos + 1][0] == '\0'); |
|
738 |
else if (argv[pos][0] == '-' && argv[pos][2] == '\0') |
|
739 |
{
|
|
740 |
if (test_unop (argv[pos])) |
|
741 |
return (unary_operator ()); |
|
742 |
else
|
|
743 |
test_syntax_error (_("%s: unary operator expected"), argv[pos]); |
|
744 |
}
|
|
745 |
else
|
|
746 |
test_syntax_error (_("%s: unary operator expected"), argv[pos]); |
|
747 |
||
748 |
return (0); |
|
749 |
}
|
|
750 |
||
751 |
#define ANDOR(s) (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o'))
|
|
752 |
||
753 |
/* This could be augmented to handle `-t' as equivalent to `-t 1', but
|
|
754 |
POSIX requires that `-t' be given an argument. */
|
|
755 |
#define ONE_ARG_TEST(s) ((s)[0] != '\0')
|
|
756 |
||
757 |
static int |
|
758 |
three_arguments () |
|
759 |
{
|
|
760 |
int value; |
|
761 |
||
762 |
if (test_binop (argv[pos+1])) |
|
763 |
{
|
|
764 |
value = binary_operator (); |
|
765 |
pos = argc; |
|
766 |
}
|
|
767 |
else if (ANDOR (argv[pos+1])) |
|
768 |
{
|
|
769 |
if (argv[pos+1][1] == 'a') |
|
770 |
value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]); |
|
771 |
else
|
|
772 |
value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]); |
|
773 |
pos = argc; |
|
774 |
}
|
|
775 |
else if (argv[pos][0] == '!' && argv[pos][1] == '\0') |
|
776 |
{
|
|
777 |
advance (1); |
|
778 |
value = !two_arguments (); |
|
779 |
}
|
|
780 |
else if (argv[pos][0] == '(' && argv[pos+2][0] == ')') |
|
781 |
{
|
|
782 |
value = ONE_ARG_TEST(argv[pos+1]); |
|
783 |
pos = argc; |
|
784 |
}
|
|
785 |
else
|
|
786 |
test_syntax_error (_("%s: binary operator expected"), argv[pos+1]); |
|
787 |
||
788 |
return (value); |
|
789 |
}
|
|
790 |
||
791 |
/* This is an implementation of a Posix.2 proposal by David Korn. */
|
|
792 |
static int |
|
793 |
posixtest () |
|
794 |
{
|
|
795 |
int value; |
|
796 |
||
797 |
switch (argc - 1) /* one extra passed in */ |
|
798 |
{
|
|
799 |
case 0: |
|
800 |
value = FALSE; |
|
801 |
pos = argc; |
|
802 |
break; |
|
803 |
||
804 |
case 1: |
|
805 |
value = ONE_ARG_TEST(argv[1]); |
|
806 |
pos = argc; |
|
807 |
break; |
|
808 |
||
809 |
case 2: |
|
810 |
value = two_arguments (); |
|
811 |
pos = argc; |
|
812 |
break; |
|
813 |
||
814 |
case 3: |
|
815 |
value = three_arguments (); |
|
816 |
break; |
|
817 |
||
818 |
case 4: |
|
819 |
if (argv[pos][0] == '!' && argv[pos][1] == '\0') |
|
820 |
{
|
|
821 |
advance (1); |
|
822 |
value = !three_arguments (); |
|
823 |
break; |
|
824 |
}
|
|
825 |
/* FALLTHROUGH */
|
|
826 |
default: |
|
827 |
value = expr (); |
|
828 |
}
|
|
829 |
||
830 |
return (value); |
|
831 |
}
|
|
832 |
||
833 |
/*
|
|
834 |
* [:
|
|
835 |
* '[' expr ']'
|
|
836 |
* test:
|
|
837 |
* test expr
|
|
838 |
*/
|
|
839 |
int
|
|
840 |
test_command (margc, margv) |
|
841 |
int margc; |
|
842 |
char **margv; |
|
843 |
{
|
|
844 |
int value; |
|
845 |
int code; |
|
846 |
||
847 |
USE_VAR(margc); |
|
848 |
||
849 |
code = setjmp_nosigs (test_exit_buf); |
|
850 |
||
851 |
if (code) |
|
852 |
return (test_error_return); |
|
853 |
||
854 |
argv = margv; |
|
855 |
||
856 |
if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0') |
|
857 |
{
|
|
858 |
--margc; |
|
859 |
||
860 |
if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1])) |
|
861 |
test_syntax_error (_("missing `]'"), (char *)NULL); |
|
862 |
||
863 |
if (margc < 2) |
|
864 |
test_exit (SHELL_BOOLEAN (FALSE)); |
|
865 |
}
|
|
866 |
||
867 |
argc = margc; |
|
868 |
pos = 1; |
|
869 |
||
870 |
if (pos >= argc) |
|
871 |
test_exit (SHELL_BOOLEAN (FALSE)); |
|
872 |
||
873 |
noeval = 0; |
|
874 |
value = posixtest (); |
|
875 |
||
876 |
if (pos != argc) |
|
877 |
test_syntax_error (_("too many arguments"), (char *)NULL); |
|
878 |
||
879 |
test_exit (SHELL_BOOLEAN (value)); |
|
880 |
}
|