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 }