After looking at the sbase TODO I threw together test(1). As far as I
can tell it's POSIX compliant. I used the POSIX description[0] to
write it. It exits 0 for true, 1 for false, and 2 for bad input. Let
me know what's good and what's bad. I'll work on addresing those
concerns and getting it into sbase format so I can submit a patch.

It specifically doesn't support parentheses and the -o and -a flags as
1) they are obsolete xsi extensions[1]
2) boolean operators are and should be handled by the shell. e.g.  [
foo = bar ] || [ 4 -lt 5 ]

-emg

[0] http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
[1] http://pubs.opengroup.org/onlinepubs/9699919799/help/codes.html#OB XSI
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

int unary_b(char *s) { struct stat buf; if ( stat(s, &buf)) return 1; return !(S_ISBLK  (buf.st_mode)); }
int unary_c(char *s) { struct stat buf; if ( stat(s, &buf)) return 1; return !(S_ISCHR  (buf.st_mode)); }
int unary_d(char *s) { struct stat buf; if ( stat(s, &buf)) return 1; return !(S_ISDIR  (buf.st_mode)); }
int unary_e(char *s) { struct stat buf; if ( stat(s, &buf)) return 1; return !(                     1); }
int unary_f(char *s) { struct stat buf; if ( stat(s, &buf)) return 1; return !(S_ISREG  (buf.st_mode)); }
int unary_g(char *s) { struct stat buf; if ( stat(s, &buf)) return 1; return !(S_ISGID & buf.st_mode ); }
int unary_h(char *s) { struct stat buf; if (lstat(s, &buf)) return 1; return !(S_ISLNK  (buf.st_mode)); }
int unary_p(char *s) { struct stat buf; if ( stat(s, &buf)) return 1; return !(S_ISFIFO (buf.st_mode)); }
int unary_S(char *s) { struct stat buf; if ( stat(s, &buf)) return 1; return !(S_ISSOCK (buf.st_mode)); }
int unary_s(char *s) { struct stat buf; if ( stat(s, &buf)) return 1; return !(          buf.st_size ); }
int unary_u(char *s) { struct stat buf; if ( stat(s, &buf)) return 1; return !(S_ISUID & buf.st_mode ); }

int unary_n(char *s) { return ! strlen(s); }
int unary_z(char *s) { return !!strlen(s); }

int unary_r(char *s) { return !!access(s, R_OK); }
int unary_w(char *s) { return !!access(s, W_OK); }
int unary_x(char *s) { return !!access(s, X_OK); }

int unary_t(char *s) {
    char *p;
    int fd = strtol(s, &p, 0);

    if (!*s || *p)
        return 0;

    return !isatty(fd);
}

int binary_se(char *s1, char *s2) { return !!strcmp(s1, s2); }
int binary_sn(char *s1, char *s2) { return ! strcmp(s1, s2); }

int two_nums(char *s1, char *s2, int *a, int *b)
{
    char *p, *q;
    *a = strtol(s1, &p, 0);
    *b = strtol(s2, &q, 0);

    if (!*s1 || *p || !*s2 || *q)
        return -1;
    return 0;
}

int binary_eq(char *s1, char *s2) { int a, b; if (two_nums(s1, s2, &a, &b)) return 2; return !(a == b); }
int binary_ne(char *s1, char *s2) { int a, b; if (two_nums(s1, s2, &a, &b)) return 2; return !(a != b); }
int binary_gt(char *s1, char *s2) { int a, b; if (two_nums(s1, s2, &a, &b)) return 2; return !(a >  b); }
int binary_ge(char *s1, char *s2) { int a, b; if (two_nums(s1, s2, &a, &b)) return 2; return !(a >= b); }
int binary_lt(char *s1, char *s2) { int a, b; if (two_nums(s1, s2, &a, &b)) return 2; return !(a <  b); }
int binary_le(char *s1, char *s2) { int a, b; if (two_nums(s1, s2, &a, &b)) return 2; return !(a <= b); }

typedef struct {
    char *name;
    int (*func)();
} Test;

Test unary[] = {
    { "-b" , unary_b},
    { "-c" , unary_c},
    { "-d" , unary_d},
    { "-e" , unary_e},
    { "-f" , unary_f},
    { "-g" , unary_g},
    { "-h" , unary_h},
    { "-L" , unary_h},
    { "-n" , unary_n},
    { "-p" , unary_p},
    { "-r" , unary_r},
    { "-S" , unary_S},
    { "-s" , unary_s},
    { "-t" , unary_t},
    { "-u" , unary_u},
    { "-w" , unary_w},
    { "-x" , unary_x},
    { "-z" , unary_z},

    { NULL, NULL },
};
Test binary[] = {
    { "="  , binary_se },
    { "!=" , binary_sn },
    { "-eq", binary_eq },
    { "-ne", binary_ne },
    { "-gt", binary_gt },
    { "-ge", binary_ge },
    { "-lt", binary_lt },
    { "-le", binary_le },

    { NULL, NULL },
};

Test *find_test(Test *tests, char *name)
{
    for (Test *t = tests; t->name; ++t)
        if (!strcmp(t->name, name))
            return t;
    return NULL;
}

int noarg(char **argv)
{
    return 1;
}

int onearg(char **argv)
{
    return !strlen(*argv);
}

int twoarg(char **argv)
{
    if (!strcmp(*argv, "!"))
        return !onearg(argv + 1);

    Test *t = find_test(unary, *argv);
    if (t)
        return t->func(argv[1]);

    return 2;
}

int threearg(char **argv)
{
    Test *t = find_test(binary, argv[1]);
    if (t)
        return t->func(argv[0], argv[2]);

    if (!strcmp(*argv, "!"))
        return !twoarg(argv + 1);

    return 2;
}

int fourarg(char **argv)
{
    if (!strcmp(*argv, "!"))
        return !threearg(argv + 1);

    return 2;
}

int (*nargs[])(char**) = {
    [0] = noarg,
    [1] = onearg,
    [2] = twoarg,
    [3] = threearg,
    [4] = fourarg,
};

int main(int argc, char **argv)
{
    if (argv[0][strlen(argv[0]) - 1] == '[') {
        if (strcmp(argv[--argc], "]"))
            return 2;
        argv[argc] = NULL;
    }

    --argc; ++argv;

    if (argc > 4)
        return 2;

    return nargs[argc](argv);
}

Reply via email to