test.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
test.c (6472B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <sys/stat.h>
3
4 #include <ctype.h>
5 #include <fcntl.h>
6 #include <string.h>
7 #include <unistd.h>
8
9 #include "util.h"
10
11 static int
12 intcmp(char *a, char *b)
13 {
14 char *s;
15 int asign = *a == '-' ? -1 : 1;
16 int bsign = *b == '-' ? -1 : 1;
17
18 if (*a == '-' || *a == '+') a += 1;
19 if (*b == '-' || *b == '+') b += 1;
20
21 if (!*a || !*b)
22 goto noint;
23 for (s = a; *s; s++)
24 if (!isdigit(*s))
25 goto noint;
26 for (s = b; *s; s++)
27 if (!isdigit(*s))
28 goto noint;
29
30 while (*a == '0') a++;
31 while (*b == '0') b++;
32 asign *= !!*a;
33 bsign *= !!*b;
34
35 if (asign != bsign)
36 return asign < bsign ? -1 : 1;
37 else if (strlen(a) != strlen(b))
38 return asign * (strlen(a) < strlen(b) ? -1 : 1);
39 else
40 return asign * strcmp(a, b);
41
42 noint:
43 enprintf(2, "expected integer operands\n");
44
45 return 0; /* not reached */
46 }
47
48 static int
49 mtimecmp(struct stat *buf1, struct stat *buf2)
50 {
51 if (buf1->st_mtime < buf2->st_mtime) return -1;
52 if (buf1->st_mtime > buf2->st_mtime) return +1;
53 #ifdef st_mtime
54 if (buf1->st_mtim.tv_nsec < buf2->st_mtim.tv_nsec) return -1;
55 if (buf1->st_mtim.tv_nsec > buf2->st_mtim.tv_nsec) return +1;
56 #endif
57 return 0;
58 }
59
60 static int unary_b(char *s) { struct stat buf; if ( stat(s, &buf)) return 0; return S_ISBLK (buf.st_mode); }
61 static int unary_c(char *s) { struct stat buf; if ( stat(s, &buf)) return 0; return S_ISCHR (buf.st_mode); }
62 static int unary_d(char *s) { struct stat buf; if ( stat(s, &buf)) return 0; return S_ISDIR (buf.st_mode); }
63 static int unary_f(char *s) { struct stat buf; if ( stat(s, &buf)) return 0; return S_ISREG (buf.st_mode); }
64 static int unary_g(char *s) { struct stat buf; if ( stat(s, &buf)) return 0; return S_ISGID & buf.st_mode ; }
65 static int unary_h(char *s) { struct stat buf; if (lstat(s, &buf)) return 0; return S_ISLNK (buf.st_mode); }
66 static int unary_k(char *s) { struct stat buf; if ( stat(s, &buf)) return 0; return S_ISVTX & buf.st_mode ; }
67 static int unary_p(char *s) { struct stat buf; if ( stat(s, &buf)) return 0; return S_ISFIFO (buf.st_mode); }
68 static int unary_S(char *s) { struct stat buf; if ( stat(s, &buf)) return 0; return S_ISSOCK (buf.st_mode); }
69 static int unary_s(char *s) { struct stat buf; if ( stat(s, &buf)) return 0; return buf.st_size ; }
70 static int unary_u(char *s) { struct stat buf; if ( stat(s, &buf)) return 0; return S_ISUID & buf.st_mode ; }
71
72 static int unary_n(char *s) { return *s; }
73 static int unary_z(char *s) { return !*s; }
74
75 static int unary_e(char *s) { return !faccessat(AT_FDCWD, s, F_OK, AT_EACCESS); }
76 static int unary_r(char *s) { return !faccessat(AT_FDCWD, s, R_OK, AT_EACCESS); }
77 static int unary_w(char *s) { return !faccessat(AT_FDCWD, s, W_OK, AT_EACCESS); }
78 static int unary_x(char *s) { return !faccessat(AT_FDCWD, s, X_OK, AT_EACCESS); }
79
80 static int unary_t(char *s) { int fd = enstrtonum(2, s, 0, INT_MAX); return isatty(fd); }
81
82 static int binary_se(char *s1, char *s2) { return !strcmp(s1, s2); }
83 static int binary_sn(char *s1, char *s2) { return strcmp(s1, s2); }
84
85 static int binary_eq(char *s1, char *s2) { return intcmp(s1, s2) == 0; }
86 static int binary_ne(char *s1, char *s2) { return intcmp(s1, s2) != 0; }
87 static int binary_gt(char *s1, char *s2) { return intcmp(s1, s2) > 0; }
88 static int binary_ge(char *s1, char *s2) { return intcmp(s1, s2) >= 0; }
89 static int binary_lt(char *s1, char *s2) { return intcmp(s1, s2) < 0; }
90 static int binary_le(char *s1, char *s2) { return intcmp(s1, s2) <= 0; }
91
92 static int
93 binary_ef(char *s1, char *s2)
94 {
95 struct stat buf1, buf2;
96 if (stat(s1, &buf1) || stat(s2, &buf2)) return 0;
97 return buf1.st_dev == buf2.st_dev && buf1.st_ino == buf2.st_ino;
98 }
99
100 static int
101 binary_ot(char *s1, char *s2)
102 {
103 struct stat buf1, buf2;
104 if (stat(s1, &buf1) || stat(s2, &buf2)) return 0;
105 return mtimecmp(&buf1, &buf2) < 0;
106 }
107
108 static int
109 binary_nt(char *s1, char *s2)
110 {
111 struct stat buf1, buf2;
112 if (stat(s1, &buf1) || stat(s2, &buf2)) return 0;
113 return mtimecmp(&buf1, &buf2) > 0;
114 }
115
116 struct test {
117 char *name;
118 union {
119 int (*u)(char *);
120 int (*b)(char *, char *);
121 } func;
122 };
123
124 static struct test unary[] = {
125 { "-b", { .u = unary_b } },
126 { "-c", { .u = unary_c } },
127 { "-d", { .u = unary_d } },
128 { "-e", { .u = unary_e } },
129 { "-f", { .u = unary_f } },
130 { "-g", { .u = unary_g } },
131 { "-h", { .u = unary_h } },
132 { "-k", { .u = unary_k } },
133 { "-L", { .u = unary_h } },
134 { "-n", { .u = unary_n } },
135 { "-p", { .u = unary_p } },
136 { "-r", { .u = unary_r } },
137 { "-S", { .u = unary_S } },
138 { "-s", { .u = unary_s } },
139 { "-t", { .u = unary_t } },
140 { "-u", { .u = unary_u } },
141 { "-w", { .u = unary_w } },
142 { "-x", { .u = unary_x } },
143 { "-z", { .u = unary_z } },
144
145 { NULL },
146 };
147
148 static struct test binary[] = {
149 { "=" , { .b = binary_se } },
150 { "!=" , { .b = binary_sn } },
151 { "-eq", { .b = binary_eq } },
152 { "-ne", { .b = binary_ne } },
153 { "-gt", { .b = binary_gt } },
154 { "-ge", { .b = binary_ge } },
155 { "-lt", { .b = binary_lt } },
156 { "-le", { .b = binary_le } },
157 { "-ef", { .b = binary_ef } },
158 { "-ot", { .b = binary_ot } },
159 { "-nt", { .b = binary_nt } },
160
161 { NULL },
162 };
163
164 static struct test *
165 find_test(struct test *tests, char *name)
166 {
167 struct test *t;
168
169 for (t = tests; t->name; t++)
170 if (!strcmp(t->name, name))
171 return t;
172
173 return NULL;
174 }
175
176 static int
177 noarg(char *argv[])
178 {
179 return 0;
180 }
181
182 static int
183 onearg(char *argv[])
184 {
185 return unary_n(argv[0]);
186 }
187
188 static int
189 twoarg(char *argv[])
190 {
191 struct test *t;
192
193 if (!strcmp(argv[0], "!"))
194 return !onearg(argv + 1);
195
196 if ((t = find_test(unary, *argv)))
197 return t->func.u(argv[1]);
198
199 enprintf(2, "bad unary test %s\n", argv[0]);
200
201 return 0; /* not reached */
202 }
203
204 static int
205 threearg(char *argv[])
206 {
207 struct test *t = find_test(binary, argv[1]);
208
209 if (t)
210 return t->func.b(argv[0], argv[2]);
211
212 if (!strcmp(argv[0], "!"))
213 return !twoarg(argv + 1);
214
215 enprintf(2, "bad binary test %s\n", argv[1]);
216
217 return 0; /* not reached */
218 }
219
220 static int
221 fourarg(char *argv[])
222 {
223 if (!strcmp(argv[0], "!"))
224 return !threearg(argv + 1);
225
226 enprintf(2, "too many arguments\n");
227
228 return 0; /* not reached */
229 }
230
231 int
232 main(int argc, char *argv[])
233 {
234 int (*narg[])(char *[]) = { noarg, onearg, twoarg, threearg, fourarg };
235 size_t len;
236
237 argv0 = *argv, argv0 ? (argc--, argv++) : (void *)0;
238
239 len = argv0 ? strlen(argv0) : 0;
240 if (len && argv0[--len] == '[' && (!len || argv0[--len] == '/') && strcmp(argv[--argc], "]"))
241 enprintf(2, "no matching ]\n");
242
243 if (argc > 4)
244 enprintf(2, "too many arguments\n");
245
246 return !narg[argc](argv);
247 }