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 }