master xplshn/aruu / cmd / posix / test.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <sys/stat.h>
  4
  5#include <ctype.h>
  6#include <fcntl.h>
  7#include <string.h>
  8#include <unistd.h>
  9
 10#include "util.h"
 11
 12static int
 13intcmp(char *a, char *b)
 14{
 15  char *s;
 16  int   asign = *a == '-' ? -1 : 1;
 17  int   bsign = *b == '-' ? -1 : 1;
 18
 19  if (*a == '-' || *a == '+')
 20    a += 1;
 21  if (*b == '-' || *b == '+')
 22    b += 1;
 23
 24  if (!*a || !*b)
 25    goto noint;
 26  for (s = a; *s; s++)
 27    if (!isdigit(*s))
 28      goto noint;
 29  for (s = b; *s; s++)
 30    if (!isdigit(*s))
 31      goto noint;
 32
 33  while (*a == '0')
 34    a++;
 35  while (*b == '0')
 36    b++;
 37  asign *= !!*a;
 38  bsign *= !!*b;
 39
 40  if (asign != bsign)
 41    return asign < bsign ? -1 : 1;
 42  else if (strlen(a) != strlen(b))
 43    return asign * (strlen(a) < strlen(b) ? -1 : 1);
 44  else
 45    return asign * strcmp(a, b);
 46
 47noint:
 48  enprintf(2, "expected integer operands\n");
 49
 50  return 0; /* not reached */
 51}
 52
 53static int
 54mtimecmp(struct stat *buf1, struct stat *buf2)
 55{
 56  if (buf1->st_mtime < buf2->st_mtime)
 57    return -1;
 58  if (buf1->st_mtime > buf2->st_mtime)
 59    return +1;
 60#ifdef st_mtime
 61  if (buf1->st_mtim.tv_nsec < buf2->st_mtim.tv_nsec)
 62    return -1;
 63  if (buf1->st_mtim.tv_nsec > buf2->st_mtim.tv_nsec)
 64    return +1;
 65#endif
 66  return 0;
 67}
 68
 69static int
 70unary_b(char *s)
 71{
 72  struct stat buf;
 73  if (stat(s, &buf))
 74    return 0;
 75  return S_ISBLK(buf.st_mode);
 76}
 77static int
 78unary_c(char *s)
 79{
 80  struct stat buf;
 81  if (stat(s, &buf))
 82    return 0;
 83  return S_ISCHR(buf.st_mode);
 84}
 85static int
 86unary_d(char *s)
 87{
 88  struct stat buf;
 89  if (stat(s, &buf))
 90    return 0;
 91  return S_ISDIR(buf.st_mode);
 92}
 93static int
 94unary_f(char *s)
 95{
 96  struct stat buf;
 97  if (stat(s, &buf))
 98    return 0;
 99  return S_ISREG(buf.st_mode);
100}
101static int
102unary_g(char *s)
103{
104  struct stat buf;
105  if (stat(s, &buf))
106    return 0;
107  return S_ISGID & buf.st_mode;
108}
109static int
110unary_h(char *s)
111{
112  struct stat buf;
113  if (lstat(s, &buf))
114    return 0;
115  return S_ISLNK(buf.st_mode);
116}
117static int
118unary_k(char *s)
119{
120  struct stat buf;
121  if (stat(s, &buf))
122    return 0;
123  return S_ISVTX & buf.st_mode;
124}
125static int
126unary_p(char *s)
127{
128  struct stat buf;
129  if (stat(s, &buf))
130    return 0;
131  return S_ISFIFO(buf.st_mode);
132}
133static int
134unary_S(char *s)
135{
136  struct stat buf;
137  if (stat(s, &buf))
138    return 0;
139  return S_ISSOCK(buf.st_mode);
140}
141static int
142unary_s(char *s)
143{
144  struct stat buf;
145  if (stat(s, &buf))
146    return 0;
147  return buf.st_size;
148}
149static int
150unary_u(char *s)
151{
152  struct stat buf;
153  if (stat(s, &buf))
154    return 0;
155  return S_ISUID & buf.st_mode;
156}
157
158static int
159unary_n(char *s)
160{
161  return *s;
162}
163static int
164unary_z(char *s)
165{
166  return !*s;
167}
168
169static int
170unary_e(char *s)
171{
172  return !faccessat(AT_FDCWD, s, F_OK, AT_EACCESS);
173}
174static int
175unary_r(char *s)
176{
177  return !faccessat(AT_FDCWD, s, R_OK, AT_EACCESS);
178}
179static int
180unary_w(char *s)
181{
182  return !faccessat(AT_FDCWD, s, W_OK, AT_EACCESS);
183}
184static int
185unary_x(char *s)
186{
187  return !faccessat(AT_FDCWD, s, X_OK, AT_EACCESS);
188}
189
190static int
191unary_t(char *s)
192{
193  int fd = enstrtonum(2, s, 0, INT_MAX);
194  return isatty(fd);
195}
196
197static int
198binary_se(char *s1, char *s2)
199{
200  return !strcmp(s1, s2);
201}
202static int
203binary_sn(char *s1, char *s2)
204{
205  return strcmp(s1, s2);
206}
207
208static int
209binary_eq(char *s1, char *s2)
210{
211  return intcmp(s1, s2) == 0;
212}
213static int
214binary_ne(char *s1, char *s2)
215{
216  return intcmp(s1, s2) != 0;
217}
218static int
219binary_gt(char *s1, char *s2)
220{
221  return intcmp(s1, s2) > 0;
222}
223static int
224binary_ge(char *s1, char *s2)
225{
226  return intcmp(s1, s2) >= 0;
227}
228static int
229binary_lt(char *s1, char *s2)
230{
231  return intcmp(s1, s2) < 0;
232}
233static int
234binary_le(char *s1, char *s2)
235{
236  return intcmp(s1, s2) <= 0;
237}
238
239static int
240binary_ef(char *s1, char *s2)
241{
242  struct stat buf1, buf2;
243  if (stat(s1, &buf1) || stat(s2, &buf2))
244    return 0;
245  return buf1.st_dev == buf2.st_dev && buf1.st_ino == buf2.st_ino;
246}
247
248static int
249binary_ot(char *s1, char *s2)
250{
251  struct stat buf1, buf2;
252  if (stat(s1, &buf1) || stat(s2, &buf2))
253    return 0;
254  return mtimecmp(&buf1, &buf2) < 0;
255}
256
257static int
258binary_nt(char *s1, char *s2)
259{
260  struct stat buf1, buf2;
261  if (stat(s1, &buf1) || stat(s2, &buf2))
262    return 0;
263  return mtimecmp(&buf1, &buf2) > 0;
264}
265
266struct test {
267  char *name;
268  union {
269    int (*u)(char *);
270    int (*b)(char *, char *);
271  } func;
272};
273
274static struct test unary[] = {
275    {"-b", {.u = unary_b}},
276    {"-c", {.u = unary_c}},
277    {"-d", {.u = unary_d}},
278    {"-e", {.u = unary_e}},
279    {"-f", {.u = unary_f}},
280    {"-g", {.u = unary_g}},
281    {"-h", {.u = unary_h}},
282    {"-k", {.u = unary_k}},
283    {"-L", {.u = unary_h}},
284    {"-n", {.u = unary_n}},
285    {"-p", {.u = unary_p}},
286    {"-r", {.u = unary_r}},
287    {"-S", {.u = unary_S}},
288    {"-s", {.u = unary_s}},
289    {"-t", {.u = unary_t}},
290    {"-u", {.u = unary_u}},
291    {"-w", {.u = unary_w}},
292    {"-x", {.u = unary_x}},
293    {"-z", {.u = unary_z}},
294
295    {NULL},
296};
297
298static struct test binary[] = {
299    {"=", {.b = binary_se}},
300    {"!=", {.b = binary_sn}},
301    {"-eq", {.b = binary_eq}},
302    {"-ne", {.b = binary_ne}},
303    {"-gt", {.b = binary_gt}},
304    {"-ge", {.b = binary_ge}},
305    {"-lt", {.b = binary_lt}},
306    {"-le", {.b = binary_le}},
307    {"-ef", {.b = binary_ef}},
308    {"-ot", {.b = binary_ot}},
309    {"-nt", {.b = binary_nt}},
310
311    {NULL},
312};
313
314static struct test *
315find_test(struct test *tests, char *name)
316{
317  struct test *t;
318
319  for (t = tests; t->name; t++)
320    if (!strcmp(t->name, name))
321      return t;
322
323  return NULL;
324}
325
326static int
327noarg(char *argv[])
328{
329  (void)argv;
330  return 0;
331}
332
333static int
334onearg(char *argv[])
335{
336  return unary_n(argv[0]);
337}
338
339static int
340twoarg(char *argv[])
341{
342  struct test *t;
343
344  if (!strcmp(argv[0], "!"))
345    return !onearg(argv + 1);
346
347  if ((t = find_test(unary, *argv)))
348    return t->func.u(argv[1]);
349
350  enprintf(2, "bad unary test %s\n", argv[0]);
351
352  return 0; /* not reached */
353}
354
355static int
356threearg(char *argv[])
357{
358  struct test *t = find_test(binary, argv[1]);
359
360  if (t)
361    return t->func.b(argv[0], argv[2]);
362
363  if (!strcmp(argv[0], "!"))
364    return !twoarg(argv + 1);
365
366  enprintf(2, "bad binary test %s\n", argv[1]);
367
368  return 0; /* not reached */
369}
370
371static int
372fourarg(char *argv[])
373{
374  if (!strcmp(argv[0], "!"))
375    return !threearg(argv + 1);
376
377  enprintf(2, "too many arguments\n");
378
379  return 0; /* not reached */
380}
381
382// ?man test: evaluate condition
383// ?man check file types and compare values, returning 0 or 1
384int
385main(int argc, char *argv[])
386{
387  int (*narg[])(char *[]) = {noarg, onearg, twoarg, threearg, fourarg};
388  size_t len;
389
390  argv0 = *argv, argv0 ? (argc--, argv++) : (void *)0;
391
392  len = argv0 ? strlen(argv0) : 0;
393  if (len && argv0[--len] == '[' && (!len || argv0[--len] == '/') && strcmp(argv[--argc], "]"))
394    enprintf(2, "no matching ]\n");
395
396  if (argc > 4)
397    enprintf(2, "too many arguments\n");
398
399  return !narg[argc](argv);
400}