sbase

suckless unix tools
git clone git://git.2f30.org/sbase
Log | Files | Refs | README | 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 }