Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */ |
2 | ||
b3967821 | 3 | /*- |
984263bc MD |
4 | * test(1); version 7-like -- author Erik Baalbergen |
5 | * modified by Eric Gisin to be used as built-in. | |
6 | * modified by Arnold Robbins to add SVR3 compatibility | |
7 | * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). | |
8 | * modified by J.T. Conklin for NetBSD. | |
9 | * | |
10 | * This program is in the Public Domain. | |
1de703da | 11 | * |
7f0f5223 | 12 | * $FreeBSD: head/bin/test/test.c 298232 2016-04-19 00:38:07Z araujo $ |
d5d041ec PA |
13 | */ |
14 | /* | |
15 | * Important: This file is used both as a standalone program /bin/test and | |
16 | * as a builtin for /bin/sh (#define SHELL). | |
984263bc MD |
17 | */ |
18 | ||
984263bc MD |
19 | #include <sys/types.h> |
20 | #include <sys/stat.h> | |
21 | ||
22 | #include <ctype.h> | |
23 | #include <err.h> | |
24 | #include <errno.h> | |
b3967821 | 25 | #include <inttypes.h> |
984263bc MD |
26 | #include <limits.h> |
27 | #include <stdarg.h> | |
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
31 | #include <unistd.h> | |
32 | ||
33 | #ifdef SHELL | |
34 | #define main testcmd | |
35 | #include "bltin/bltin.h" | |
36 | #else | |
b3967821 PA |
37 | #include <locale.h> |
38 | ||
39 | static void error(const char *, ...) __dead2 __printf0like(1, 2); | |
984263bc MD |
40 | |
41 | static void | |
984263bc | 42 | error(const char *msg, ...) |
984263bc MD |
43 | { |
44 | va_list ap; | |
984263bc | 45 | |
984263bc | 46 | va_start(ap, msg); |
984263bc MD |
47 | verrx(2, msg, ap); |
48 | /*NOTREACHED*/ | |
49 | va_end(ap); | |
50 | } | |
51 | #endif | |
52 | ||
53 | /* test(1) accepts the following grammar: | |
54 | oexpr ::= aexpr | aexpr "-o" oexpr ; | |
55 | aexpr ::= nexpr | nexpr "-a" aexpr ; | |
56 | nexpr ::= primary | "!" primary | |
57 | primary ::= unary-operator operand | |
58 | | operand binary-operator operand | |
59 | | operand | |
60 | | "(" oexpr ")" | |
61 | ; | |
62 | unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| | |
63 | "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; | |
64 | ||
65 | binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| | |
66 | "-nt"|"-ot"|"-ef"; | |
67 | operand ::= <any legal UNIX file name> | |
68 | */ | |
69 | ||
7f0f5223 KP |
70 | enum token_types { |
71 | UNOP = 0x100, | |
72 | BINOP = 0x200, | |
73 | BUNOP = 0x300, | |
74 | BBINOP = 0x400, | |
75 | PAREN = 0x500 | |
76 | }; | |
77 | ||
984263bc MD |
78 | enum token { |
79 | EOI, | |
7f0f5223 KP |
80 | OPERAND, |
81 | FILRD = UNOP + 1, | |
984263bc MD |
82 | FILWR, |
83 | FILEX, | |
84 | FILEXIST, | |
85 | FILREG, | |
86 | FILDIR, | |
87 | FILCDEV, | |
88 | FILBDEV, | |
89 | FILFIFO, | |
90 | FILSOCK, | |
91 | FILSYM, | |
92 | FILGZ, | |
93 | FILTT, | |
94 | FILSUID, | |
95 | FILSGID, | |
96 | FILSTCK, | |
984263bc MD |
97 | STREZ, |
98 | STRNZ, | |
7f0f5223 KP |
99 | FILUID, |
100 | FILGID, | |
101 | FILNT = BINOP + 1, | |
102 | FILOT, | |
103 | FILEQ, | |
984263bc MD |
104 | STREQ, |
105 | STRNE, | |
106 | STRLT, | |
107 | STRGT, | |
108 | INTEQ, | |
109 | INTNE, | |
110 | INTGE, | |
111 | INTGT, | |
112 | INTLE, | |
113 | INTLT, | |
7f0f5223 KP |
114 | UNOT = BUNOP + 1, |
115 | BAND = BBINOP + 1, | |
984263bc | 116 | BOR, |
7f0f5223 KP |
117 | LPAREN = PAREN + 1, |
118 | RPAREN | |
984263bc MD |
119 | }; |
120 | ||
7f0f5223 KP |
121 | #define TOKEN_TYPE(token) ((token) & 0xff00) |
122 | ||
123 | static const struct t_op { | |
124 | char op_text[2]; | |
125 | short op_num; | |
126 | } ops1[] = { | |
127 | {"=", STREQ}, | |
128 | {"<", STRLT}, | |
129 | {">", STRGT}, | |
130 | {"!", UNOT}, | |
131 | {"(", LPAREN}, | |
132 | {")", RPAREN}, | |
133 | }, opsm1[] = { | |
134 | {"r", FILRD}, | |
135 | {"w", FILWR}, | |
136 | {"x", FILEX}, | |
137 | {"e", FILEXIST}, | |
138 | {"f", FILREG}, | |
139 | {"d", FILDIR}, | |
140 | {"c", FILCDEV}, | |
141 | {"b", FILBDEV}, | |
142 | {"p", FILFIFO}, | |
143 | {"u", FILSUID}, | |
144 | {"g", FILSGID}, | |
145 | {"k", FILSTCK}, | |
146 | {"s", FILGZ}, | |
147 | {"t", FILTT}, | |
148 | {"z", STREZ}, | |
149 | {"n", STRNZ}, | |
150 | {"h", FILSYM}, /* for backwards compat */ | |
151 | {"O", FILUID}, | |
152 | {"G", FILGID}, | |
153 | {"L", FILSYM}, | |
154 | {"S", FILSOCK}, | |
155 | {"a", BAND}, | |
156 | {"o", BOR}, | |
157 | }, ops2[] = { | |
158 | {"==", STREQ}, | |
159 | {"!=", STRNE}, | |
160 | }, opsm2[] = { | |
161 | {"eq", INTEQ}, | |
162 | {"ne", INTNE}, | |
163 | {"ge", INTGE}, | |
164 | {"gt", INTGT}, | |
165 | {"le", INTLE}, | |
166 | {"lt", INTLT}, | |
167 | {"nt", FILNT}, | |
168 | {"ot", FILOT}, | |
169 | {"ef", FILEQ}, | |
984263bc MD |
170 | }; |
171 | ||
d5d041ec PA |
172 | static int nargc; |
173 | static char **t_wp; | |
174 | static int parenlevel; | |
b3967821 PA |
175 | |
176 | static int aexpr(enum token); | |
7f0f5223 | 177 | static int binop(enum token); |
b3967821 PA |
178 | static int equalf(const char *, const char *); |
179 | static int filstat(char *, enum token); | |
180 | static int getn(const char *); | |
181 | static intmax_t getq(const char *); | |
182 | static int intcmp(const char *, const char *); | |
183 | static int isunopoperand(void); | |
184 | static int islparenoperand(void); | |
185 | static int isrparenoperand(void); | |
186 | static int newerf(const char *, const char *); | |
187 | static int nexpr(enum token); | |
188 | static int oexpr(enum token); | |
189 | static int olderf(const char *, const char *); | |
190 | static int primary(enum token); | |
191 | static void syntax(const char *, const char *); | |
192 | static enum token t_lex(char *); | |
984263bc MD |
193 | |
194 | int | |
b5744197 | 195 | main(int argc, char **argv) |
984263bc | 196 | { |
bbb1a777 | 197 | int res; |
984263bc MD |
198 | char *p; |
199 | ||
17b61719 | 200 | if ((p = strrchr(argv[0], '/')) == NULL) |
984263bc MD |
201 | p = argv[0]; |
202 | else | |
203 | p++; | |
204 | if (strcmp(p, "[") == 0) { | |
205 | if (strcmp(argv[--argc], "]") != 0) | |
206 | error("missing ]"); | |
207 | argv[argc] = NULL; | |
208 | } | |
209 | ||
210 | /* no expression => false */ | |
211 | if (--argc <= 0) | |
212 | return 1; | |
213 | ||
b3967821 PA |
214 | #ifndef SHELL |
215 | setlocale(LC_CTYPE, ""); | |
216 | #endif | |
984263bc MD |
217 | nargc = argc; |
218 | t_wp = &argv[1]; | |
b3967821 PA |
219 | parenlevel = 0; |
220 | if (nargc == 4 && strcmp(*t_wp, "!") == 0) { | |
221 | /* Things like ! "" -o x do not fit in the normal grammar. */ | |
222 | --nargc; | |
223 | ++t_wp; | |
224 | res = oexpr(t_lex(*t_wp)); | |
225 | } else | |
226 | res = !oexpr(t_lex(*t_wp)); | |
984263bc MD |
227 | |
228 | if (--nargc > 0) | |
229 | syntax(*t_wp, "unexpected operator"); | |
984263bc MD |
230 | |
231 | return res; | |
232 | } | |
233 | ||
234 | static void | |
b5744197 | 235 | syntax(const char *op, const char *msg) |
984263bc MD |
236 | { |
237 | ||
238 | if (op && *op) | |
239 | error("%s: %s", op, msg); | |
240 | else | |
241 | error("%s", msg); | |
242 | } | |
243 | ||
244 | static int | |
b5744197 | 245 | oexpr(enum token n) |
984263bc MD |
246 | { |
247 | int res; | |
248 | ||
249 | res = aexpr(n); | |
250 | if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR) | |
251 | return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) || | |
252 | res; | |
253 | t_wp--; | |
254 | nargc++; | |
255 | return res; | |
256 | } | |
257 | ||
258 | static int | |
b5744197 | 259 | aexpr(enum token n) |
984263bc MD |
260 | { |
261 | int res; | |
262 | ||
263 | res = nexpr(n); | |
264 | if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND) | |
265 | return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) && | |
266 | res; | |
267 | t_wp--; | |
268 | nargc++; | |
269 | return res; | |
270 | } | |
271 | ||
272 | static int | |
b5744197 | 273 | nexpr(enum token n) |
984263bc MD |
274 | { |
275 | if (n == UNOT) | |
276 | return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)); | |
277 | return primary(n); | |
278 | } | |
279 | ||
280 | static int | |
b5744197 | 281 | primary(enum token n) |
984263bc MD |
282 | { |
283 | enum token nn; | |
284 | int res; | |
285 | ||
286 | if (n == EOI) | |
287 | return 0; /* missing expression */ | |
288 | if (n == LPAREN) { | |
b3967821 | 289 | parenlevel++; |
984263bc | 290 | if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) == |
b3967821 PA |
291 | RPAREN) { |
292 | parenlevel--; | |
984263bc | 293 | return 0; /* missing expression */ |
b3967821 | 294 | } |
984263bc MD |
295 | res = oexpr(nn); |
296 | if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN) | |
297 | syntax(NULL, "closing paren expected"); | |
b3967821 | 298 | parenlevel--; |
984263bc MD |
299 | return res; |
300 | } | |
7f0f5223 | 301 | if (TOKEN_TYPE(n) == UNOP) { |
984263bc MD |
302 | /* unary expression */ |
303 | if (--nargc == 0) | |
7f0f5223 | 304 | syntax(NULL, "argument expected"); /* impossible */ |
984263bc MD |
305 | switch (n) { |
306 | case STREZ: | |
307 | return strlen(*++t_wp) == 0; | |
308 | case STRNZ: | |
309 | return strlen(*++t_wp) != 0; | |
310 | case FILTT: | |
311 | return isatty(getn(*++t_wp)); | |
312 | default: | |
313 | return filstat(*++t_wp, n); | |
314 | } | |
315 | } | |
316 | ||
7f0f5223 KP |
317 | nn = t_lex(nargc > 0 ? t_wp[1] : NULL); |
318 | if (TOKEN_TYPE(nn) == BINOP) | |
319 | return binop(nn); | |
984263bc MD |
320 | |
321 | return strlen(*t_wp) > 0; | |
322 | } | |
323 | ||
324 | static int | |
7f0f5223 | 325 | binop(enum token n) |
984263bc | 326 | { |
7f0f5223 | 327 | const char *opnd1, *op, *opnd2; |
984263bc MD |
328 | |
329 | opnd1 = *t_wp; | |
7f0f5223 | 330 | op = nargc > 0 ? (--nargc, *++t_wp) : NULL; |
984263bc MD |
331 | |
332 | if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL) | |
7f0f5223 | 333 | syntax(op, "argument expected"); |
984263bc | 334 | |
7f0f5223 | 335 | switch (n) { |
984263bc MD |
336 | case STREQ: |
337 | return strcmp(opnd1, opnd2) == 0; | |
338 | case STRNE: | |
339 | return strcmp(opnd1, opnd2) != 0; | |
340 | case STRLT: | |
341 | return strcmp(opnd1, opnd2) < 0; | |
342 | case STRGT: | |
343 | return strcmp(opnd1, opnd2) > 0; | |
344 | case INTEQ: | |
345 | return intcmp(opnd1, opnd2) == 0; | |
346 | case INTNE: | |
347 | return intcmp(opnd1, opnd2) != 0; | |
348 | case INTGE: | |
349 | return intcmp(opnd1, opnd2) >= 0; | |
350 | case INTGT: | |
351 | return intcmp(opnd1, opnd2) > 0; | |
352 | case INTLE: | |
353 | return intcmp(opnd1, opnd2) <= 0; | |
354 | case INTLT: | |
355 | return intcmp(opnd1, opnd2) < 0; | |
356 | case FILNT: | |
357 | return newerf (opnd1, opnd2); | |
358 | case FILOT: | |
359 | return olderf (opnd1, opnd2); | |
360 | case FILEQ: | |
361 | return equalf (opnd1, opnd2); | |
362 | default: | |
363 | abort(); | |
364 | /* NOTREACHED */ | |
365 | } | |
366 | } | |
367 | ||
368 | static int | |
b5744197 | 369 | filstat(char *nm, enum token mode) |
984263bc MD |
370 | { |
371 | struct stat s; | |
372 | ||
373 | if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) | |
374 | return 0; | |
375 | ||
376 | switch (mode) { | |
377 | case FILRD: | |
d5d041ec | 378 | return (eaccess(nm, R_OK) == 0); |
984263bc | 379 | case FILWR: |
d5d041ec | 380 | return (eaccess(nm, W_OK) == 0); |
984263bc | 381 | case FILEX: |
d5d041ec PA |
382 | /* XXX work around eaccess(2) false positives for superuser */ |
383 | if (eaccess(nm, X_OK) != 0) | |
984263bc | 384 | return 0; |
d5d041ec | 385 | if (S_ISDIR(s.st_mode) || geteuid() != 0) |
984263bc MD |
386 | return 1; |
387 | return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; | |
388 | case FILEXIST: | |
d5d041ec | 389 | return (eaccess(nm, F_OK) == 0); |
984263bc MD |
390 | case FILREG: |
391 | return S_ISREG(s.st_mode); | |
392 | case FILDIR: | |
393 | return S_ISDIR(s.st_mode); | |
394 | case FILCDEV: | |
395 | return S_ISCHR(s.st_mode); | |
396 | case FILBDEV: | |
397 | return S_ISBLK(s.st_mode); | |
398 | case FILFIFO: | |
399 | return S_ISFIFO(s.st_mode); | |
400 | case FILSOCK: | |
401 | return S_ISSOCK(s.st_mode); | |
402 | case FILSYM: | |
403 | return S_ISLNK(s.st_mode); | |
404 | case FILSUID: | |
405 | return (s.st_mode & S_ISUID) != 0; | |
406 | case FILSGID: | |
407 | return (s.st_mode & S_ISGID) != 0; | |
408 | case FILSTCK: | |
409 | return (s.st_mode & S_ISVTX) != 0; | |
410 | case FILGZ: | |
411 | return s.st_size > (off_t)0; | |
412 | case FILUID: | |
413 | return s.st_uid == geteuid(); | |
414 | case FILGID: | |
415 | return s.st_gid == getegid(); | |
416 | default: | |
417 | return 1; | |
418 | } | |
419 | } | |
420 | ||
7f0f5223 KP |
421 | static int |
422 | find_op_1char(const struct t_op *op, const struct t_op *end, const char *s) | |
984263bc | 423 | { |
7f0f5223 | 424 | char c; |
984263bc | 425 | |
7f0f5223 KP |
426 | c = s[0]; |
427 | while (op != end) { | |
428 | if (c == *op->op_text) | |
429 | return op->op_num; | |
430 | op++; | |
984263bc | 431 | } |
7f0f5223 KP |
432 | return OPERAND; |
433 | } | |
434 | ||
435 | static int | |
436 | find_op_2char(const struct t_op *op, const struct t_op *end, const char *s) | |
437 | { | |
438 | while (op != end) { | |
439 | if (s[0] == op->op_text[0] && s[1] == op->op_text[1]) | |
984263bc | 440 | return op->op_num; |
984263bc MD |
441 | op++; |
442 | } | |
984263bc | 443 | return OPERAND; |
3e3895bf | 444 | } |
984263bc | 445 | |
7f0f5223 KP |
446 | static int |
447 | find_op(const char *s) | |
448 | { | |
449 | if (s[0] == '\0') | |
450 | return OPERAND; | |
451 | else if (s[1] == '\0') | |
452 | return find_op_1char(ops1, (&ops1)[1], s); | |
453 | else if (s[2] == '\0') | |
454 | return s[0] == '-' ? find_op_1char(opsm1, (&opsm1)[1], s + 1) : | |
455 | find_op_2char(ops2, (&ops2)[1], s); | |
456 | else if (s[3] == '\0') | |
457 | return s[0] == '-' ? find_op_2char(opsm2, (&opsm2)[1], s + 1) : | |
458 | OPERAND; | |
459 | else | |
460 | return OPERAND; | |
461 | } | |
462 | ||
463 | static enum token | |
464 | t_lex(char *s) | |
465 | { | |
466 | int num; | |
467 | ||
468 | if (s == NULL) { | |
469 | return EOI; | |
470 | } | |
471 | num = find_op(s); | |
472 | if (((TOKEN_TYPE(num) == UNOP || TOKEN_TYPE(num) == BUNOP) | |
473 | && isunopoperand()) || | |
474 | (num == LPAREN && islparenoperand()) || | |
475 | (num == RPAREN && isrparenoperand())) | |
476 | return OPERAND; | |
477 | return num; | |
478 | } | |
479 | ||
984263bc | 480 | static int |
b3967821 | 481 | isunopoperand(void) |
984263bc | 482 | { |
984263bc MD |
483 | char *s; |
484 | char *t; | |
7f0f5223 | 485 | int num; |
984263bc MD |
486 | |
487 | if (nargc == 1) | |
488 | return 1; | |
984263bc | 489 | s = *(t_wp + 1); |
b3967821 PA |
490 | if (nargc == 2) |
491 | return parenlevel == 1 && strcmp(s, ")") == 0; | |
984263bc | 492 | t = *(t_wp + 2); |
7f0f5223 KP |
493 | num = find_op(s); |
494 | return TOKEN_TYPE(num) == BINOP && | |
495 | (parenlevel == 0 || t[0] != ')' || t[1] != '\0'); | |
984263bc MD |
496 | } |
497 | ||
b3967821 PA |
498 | static int |
499 | islparenoperand(void) | |
500 | { | |
b3967821 | 501 | char *s; |
7f0f5223 | 502 | int num; |
b3967821 PA |
503 | |
504 | if (nargc == 1) | |
505 | return 1; | |
506 | s = *(t_wp + 1); | |
507 | if (nargc == 2) | |
508 | return parenlevel == 1 && strcmp(s, ")") == 0; | |
509 | if (nargc != 3) | |
510 | return 0; | |
7f0f5223 KP |
511 | num = find_op(s); |
512 | return TOKEN_TYPE(num) == BINOP; | |
b3967821 PA |
513 | } |
514 | ||
515 | static int | |
516 | isrparenoperand(void) | |
517 | { | |
518 | char *s; | |
519 | ||
520 | if (nargc == 1) | |
521 | return 0; | |
522 | s = *(t_wp + 1); | |
523 | if (nargc == 2) | |
524 | return parenlevel == 1 && strcmp(s, ")") == 0; | |
525 | return 0; | |
526 | } | |
527 | ||
984263bc MD |
528 | /* atoi with error detection */ |
529 | static int | |
b5744197 | 530 | getn(const char *s) |
984263bc MD |
531 | { |
532 | char *p; | |
533 | long r; | |
534 | ||
535 | errno = 0; | |
536 | r = strtol(s, &p, 10); | |
537 | ||
b3967821 PA |
538 | if (s == p) |
539 | error("%s: bad number", s); | |
540 | ||
984263bc MD |
541 | if (errno != 0) |
542 | error((errno == EINVAL) ? "%s: bad number" : | |
543 | "%s: out of range", s); | |
544 | ||
545 | while (isspace((unsigned char)*p)) | |
546 | p++; | |
547 | ||
548 | if (*p) | |
549 | error("%s: bad number", s); | |
550 | ||
551 | return (int) r; | |
552 | } | |
553 | ||
554 | /* atoi with error detection and 64 bit range */ | |
b3967821 PA |
555 | static intmax_t |
556 | getq(const char *s) | |
984263bc MD |
557 | { |
558 | char *p; | |
b3967821 | 559 | intmax_t r; |
984263bc MD |
560 | |
561 | errno = 0; | |
b3967821 PA |
562 | r = strtoimax(s, &p, 10); |
563 | ||
564 | if (s == p) | |
565 | error("%s: bad number", s); | |
984263bc MD |
566 | |
567 | if (errno != 0) | |
568 | error((errno == EINVAL) ? "%s: bad number" : | |
569 | "%s: out of range", s); | |
570 | ||
571 | while (isspace((unsigned char)*p)) | |
572 | p++; | |
573 | ||
574 | if (*p) | |
575 | error("%s: bad number", s); | |
576 | ||
577 | return r; | |
578 | } | |
579 | ||
580 | static int | |
b5744197 | 581 | intcmp (const char *s1, const char *s2) |
984263bc | 582 | { |
b3967821 | 583 | intmax_t q1, q2; |
984263bc MD |
584 | |
585 | ||
b3967821 PA |
586 | q1 = getq(s1); |
587 | q2 = getq(s2); | |
984263bc MD |
588 | |
589 | if (q1 > q2) | |
590 | return 1; | |
591 | ||
592 | if (q1 < q2) | |
593 | return -1; | |
594 | ||
595 | return 0; | |
596 | } | |
597 | ||
598 | static int | |
b5744197 | 599 | newerf (const char *f1, const char *f2) |
984263bc MD |
600 | { |
601 | struct stat b1, b2; | |
602 | ||
603 | if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0) | |
604 | return 0; | |
605 | ||
b3967821 | 606 | if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec) |
984263bc | 607 | return 1; |
b3967821 | 608 | if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec) |
984263bc MD |
609 | return 0; |
610 | ||
b3967821 | 611 | return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec); |
984263bc MD |
612 | } |
613 | ||
614 | static int | |
b5744197 | 615 | olderf (const char *f1, const char *f2) |
984263bc MD |
616 | { |
617 | return (newerf(f2, f1)); | |
618 | } | |
619 | ||
620 | static int | |
b5744197 | 621 | equalf (const char *f1, const char *f2) |
984263bc MD |
622 | { |
623 | struct stat b1, b2; | |
624 | ||
625 | return (stat (f1, &b1) == 0 && | |
626 | stat (f2, &b2) == 0 && | |
627 | b1.st_dev == b2.st_dev && | |
628 | b1.st_ino == b2.st_ino); | |
629 | } |