Annotation of coherent/b/bin/test.c.steve, revision 1.1.1.1

1.1       root        1: /*
                      2:  * cmd/test.c
                      3:  * 5/4/93
                      4:  * Set status based on specified conditions, mostly related to files.
                      5:  * Used for control flow in shell scripts.
                      6:  * Cf. POSIX P1003.2/D11.2 section 4.62; extensions marked !POSIX below.
                      7:  * Usage: see usage() below.
                      8:  * Exit status:
                      9:  *     0       expression is true
                     10:  *     1       expression is false or absent
                     11:  *     2       syntax error or other error
                     12:  * This used to be a yacc grammar test.y, but the grammar is not really LR(1);
                     13:  * e.g. "-ne" can be used either as an ordinary argument or as a string,
                     14:  * which causes great complication in yylex()/yyparse() interaction.
                     15:  * This version uses ad hoc recursive parse instead.
                     16:  */
                     17: 
                     18: #include <stdio.h>
                     19: #include <stdlib.h>
                     20: #include <access.h>
                     21: #include <sys/stat.h>
                     22: 
                     23: #define        ERROR   2                               /* error exit status */
                     24: #define        FALSE   ""                              /* false (empty) arg */
                     25: #define        TRUE    "T"                             /* true (nonempty) arg */
                     26: #define        equal(s1, s2)   (strcmp((s1), (s2)) == 0)
                     27: 
                     28: /* Primary operators. */
                     29: typedef        struct  prim    {
                     30:        char    *p_name;                        /* operator name */
                     31:        int     (*p_fn)();                      /* function */
                     32: }      PRIM;
                     33: 
                     34: /* Forward function definitions. */
                     35: int    (*is_binary)();
                     36: int    (*is_unary)();
                     37: int    rparen();
                     38: int    test();
                     39: int    testsub();
                     40: void   usage();
                     41: 
                     42: /* Elementary test functions. */
                     43: int    x_b();
                     44: int    x_c();
                     45: int    x_d();
                     46: int    x_e();
                     47: int    x_ef();
                     48: int    x_eq();
                     49: int    x_f();
                     50: int    x_g();
                     51: int    x_ge();
                     52: int    x_gt();
                     53: int    x_k();
                     54: int    x_L();
                     55: int    x_le();
                     56: int    x_lt();
                     57: int    x_n();
                     58: int    x_ne();
                     59: int    x_nt();
                     60: int    x_ot();
                     61: int    x_p();
                     62: int    x_r();
                     63: int    x_s();
                     64: int    x_str_eq();
                     65: int    x_str_gt();
                     66: int    x_str_lt();
                     67: int    x_str_ne();
                     68: int    x_t();
                     69: int    x_u();
                     70: int    x_w();
                     71: int    x_x();
                     72: int    x_z();
                     73: 
                     74: /* Globals. */
                     75: struct stat    sb, sb2;                /* stat() buffers */
                     76: 
                     77: /* Unary primaries, not including "!"; implicit leading '-'. */
                     78: PRIM   uprims[] = {
                     79:        "b",    x_b,
                     80:        "c",    x_c,
                     81:        "d",    x_d,
                     82:        "e",    x_e,
                     83:        "f",    x_f,                    /* POSIX semantics, not BSD */
                     84:        "g",    x_g,
                     85:        "k",    x_k,                    /* !POSIX */
                     86:        "L",    x_L,                    /* !POSIX */
                     87:        "n",    x_n,
                     88:        "p",    x_p,
                     89:        "r",    x_r,
                     90:        "s",    x_s,
                     91:        "t",    x_t,                    /* POSIX semantics, arg not optional */
                     92:        "u",    x_u,
                     93:        "w",    x_w,
                     94:        "x",    x_x,
                     95:        "z",    x_z
                     96: };
                     97: #define        NUPRIM  (sizeof(uprims)/sizeof(uprims[0]))
                     98: 
                     99: /* Binary primaries, not including string binaries (below), "-a", "-o"; implicit leading '-'. */
                    100: PRIM   bprims[] = {
                    101:        "ef",   x_ef,                   /* !POSIX */
                    102:        "eq",   x_eq,
                    103:        "ge",   x_ge,
                    104:        "gt",   x_gt,
                    105:        "le",   x_le,
                    106:        "lt",   x_lt,
                    107:        "ne",   x_ne,
                    108:        "nt",   x_nt,                   /* !POSIX */
                    109:        "ot",   x_ot                    /* !POSIX */
                    110: };
                    111: #define        NBPRIM  (sizeof(bprims)/sizeof(bprims[0]))
                    112: 
                    113: /* String binary primaries, no leading '-'. */
                    114: PRIM   sprims[] = {
                    115:        "=",    x_str_eq,
                    116:        ">",    x_str_gt,               /* !POSIX */
                    117:        "<",    x_str_lt,               /* !POSIX */
                    118:        "!=",   x_str_ne
                    119: };
                    120: #define        NSPRIM  (sizeof(sprims)/sizeof(sprims[0]))
                    121: 
                    122: main(argc, argv) register int argc; char *argv[];
                    123: {
                    124:        register int n;
                    125: 
                    126:        --argc;
                    127:        if (equal(argv[0], "[")) {
                    128:                if (!equal(argv[argc], "]")) {
                    129:                        fprintf(stderr, "[: missing ]\n");
                    130:                        exit(ERROR);
                    131:                }
                    132:                argc--;
                    133:        }
                    134:        ++argv;
                    135:        if ((n = test(argc, argv)) == -1)
                    136:                usage();                /* syntax error */
                    137:        exit(!n);                       /* flip returned status */
                    138: }
                    139: 
                    140: /* Return function pointer if arg is a binary primary, else return NULL. */
                    141: int
                    142: (*is_binary(s)) register char *s;
                    143: {
                    144:        register PRIM *pp, *endp;
                    145: 
                    146:        if (*s != '-') {        /* no leading '-', search string primaries */
                    147:                pp = sprims;
                    148:                endp = &sprims[NSPRIM];
                    149:        } else {                /* leading '-', search nonstring primaries */
                    150:                ++s;            /* and skip leading '-' */
                    151:                pp = bprims;
                    152:                endp = &bprims[NBPRIM];
                    153:        }
                    154:        for ( ; pp < endp; pp++)
                    155:                if (equal(pp->p_name, s))
                    156:                        return pp->p_fn;
                    157:        return NULL;
                    158: }
                    159: 
                    160: /* Return function pointer if arg is a unary primary, else return NULL. */
                    161: int
                    162: (*is_unary(s)) register char *s;
                    163: {
                    164:        register PRIM *pp;
                    165: 
                    166:        if (*s++ != '-')
                    167:                return NULL;
                    168:        for (pp = uprims; pp < &uprims[NUPRIM]; pp++)
                    169:                if (equal(pp->p_name, s))
                    170:                        return pp->p_fn;
                    171:        return NULL;
                    172: }
                    173: 
                    174: /*
                    175:  * argv[0] is '(', so find the matching ')' and return its index.
                    176:  * Return -1 if not found.
                    177:  */
                    178: int
                    179: rparen(argc, argv) int argc; char *argv[];
                    180: {
                    181:        register int n, count;
                    182: 
                    183:        for (count = n = 1; n < argc; n++) {
                    184:                if (equal(argv[n], "("))
                    185:                        ++count;
                    186:                else if (equal(argv[n], ")") && --count == 0)
                    187:                        return n;
                    188:        }
                    189:        return -1;
                    190: }
                    191: 
                    192: /*
                    193:  * Parse and evaluate the test expression.
                    194:  * The order in which subexpressions are tried here determines the parsing.
                    195:  * Handle parens, "-a", "-o", "!" directly here.
                    196:  * Tricky stuff, this may need some tinkering.
                    197:  * Return 1 if true, 0 if false, -1 if syntax error.
                    198:  */
                    199: int
                    200: test(argc, argv) register int argc; char *argv[];
                    201: {
                    202:        register int (*fnp)();
                    203:        register int n, i;
                    204: 
                    205: #if    DEBUG
                    206:        printf("test(argc=%d, argv={ ", argc);
                    207:        for (n = 0; n < argc; n++)
                    208:                printf("%s ", argv[n]);
                    209:        printf("})\n");
                    210: #endif
                    211:        if (argc == 0)
                    212:                return 0;
                    213:        if (argc == 1)
                    214:                return argv[0][0] != 0;
                    215:        /* We can assume argc >= 2 below here. */
                    216:        if (argc == 2 && (fnp = is_unary(argv[0])) != NULL)
                    217:                return (*fnp)(argv[1]);
                    218:        if (argc == 3 && (fnp = is_binary(argv[1])) != NULL)
                    219:                return (*fnp)(argv[0], argv[2]);
                    220:        if (argc <= 4 && equal(argv[0], "!")) {
                    221:                if (argc == 2)
                    222:                        return argv[1][0] == 0;
                    223:                if (argc == 3 && (fnp = is_unary(argv[1])) != NULL)
                    224:                        return !(*fnp)(argv[2]);
                    225:                if (argc == 4 && (fnp = is_binary(argv[2])) != NULL)
                    226:                        return !(*fnp)(argv[1], argv[3]);
                    227:        }
                    228:        if (equal(argv[0], "(")) {
                    229:                /* Find the matching ")" and evaluate the subexpression. */
                    230:                if ((n = rparen(argc, argv)) == -1)
                    231:                        return -1;
                    232:                else if ((i = test(n - 1, &argv[1])) == -1)
                    233:                        return -1;
                    234:                /* Replace the subexpression by TRUE or FALSE, evaluate the rest. */
                    235:                argv[n] = (i) ? TRUE : FALSE;
                    236:                return test(argc - n, &argv[n]);
                    237:        }
                    238:        if ((n = testsub(argc, argv, "-o")) != -1)
                    239:                return n;
                    240:        if ((n = testsub(argc, argv, "-a")) != -1)
                    241:                return n;
                    242:        if (equal(argv[0], "!") && (n = test(argc-1, &argv[1])) != -1)
                    243:                return !n;
                    244: #if    DEBUG
                    245:        printf("\ttest(): failed!\n");
                    246: #endif
                    247:        return -1;
                    248: }
                    249: 
                    250: /*
                    251:  * Try to split test expression involving "-a" or "-o" into subexpressions.
                    252:  * The scan is r-to-l to make "-a" and "-o" left associative.
                    253:  * Watch out for parens.
                    254:  * Return 1 if true, 0 if false, -1 if syntax error.
                    255:  */
                    256: int
                    257: testsub(argc, argv, opt) int argc; char *argv[]; char *opt;
                    258: {
                    259:        register int n, parens, n1, n2;
                    260: 
                    261:        for (parens = 0, n = argc-1; n > 0; n--) {
                    262:                if (equal(argv[n], ")"))
                    263:                        ++parens;
                    264:                else if (equal(argv[n], "("))
                    265:                        --parens;
                    266:                else if (equal(argv[n], opt) && parens == 0 && n < argc-1) {
                    267:                        /* Try subdividing at arg n. */
                    268:                        if ((n1 = test(n, argv)) != -1
                    269:                         && (n2 = test(argc-n-1, &argv[n+1])) != -1) {
                    270: #if    DEBUG
                    271:                                printf("\ttestsub(): use %s at arg %d\n", opt, n);
                    272: #endif
                    273:                                return (equal(opt, "-o")) ? (n1 || n2) : (n1 && n2);
                    274:                        }
                    275:                }
                    276:        }
                    277:        return -1;
                    278: }
                    279: 
                    280: /* Print verbose usage message and die. */
                    281: void
                    282: usage()
                    283: {
                    284:        fprintf(stderr,
                    285:                "test: test expression syntax error\n"
                    286:                "Usage: test [ expression ]\n"
                    287:                );
                    288: #if    !DEBUG
                    289:        fprintf(stderr,
                    290:                "Unary primaries:\n"
                    291:                "\t-b file\t\tfile exists and is a block special file\n"
                    292:                "\t-c file\t\tfile exists and is a character special file\n"
                    293:                "\t-d file\t\tfile exists and is a directory\n"
                    294:                "\t-e file\t\tfile exists\n"
                    295:                "\t-f file\t\tfile exists and is a regular file\n"
                    296:                "\t-g file\t\tfile exists and is setgid\n"
                    297:                "\t-k file\t\tfile exists and has sticky bit set\t(not Posix)\n"
                    298:                "\t-L file\t\tfile is a link\t\t\t\t(not Posix)\n"
                    299:                "\t-n string\tstring length is nonzero\n"
                    300:                "\t-p file\t\tfile exists and is a named pipe (FIFO)\n"
                    301:                "\t-r file\t\tfile exists and is readable\n"
                    302:                "\t-s file\t\tfile exists and has nonzero size\n"
                    303:                "\t-t fd\t\tfd is the file descriptor of a terminal\n"
                    304:                "\t-u file\t\tfile exists and is setuid\n"
                    305:                "\t-w file\t\tfile exists and is writable\n"
                    306:                "\t-x file\t\tfile exists and is executable\n"
                    307:                "\t-z string\tstring length is zero\n"
                    308:                "\tstring\t\tstring is not the empty string\n"
                    309:                );
                    310:        fprintf(stderr,
                    311:                "Binary primaries:\n"
                    312:                "\ts1 = s2\t\tstrings s1 and s2 are identical\n"
                    313:                "\ts1 != s2\tstrings s1 and s2 are not identical\n"
                    314:                "\ts1 < s2\t\tstring s1 is less than s2\t\t(not Posix)\n"
                    315:                "\ts1 > s2\t\tstring s1 is greater than s2\t\t(not Posix)\n"
                    316:                "\tfile1 -ef file2\tfile1 and file2 are identical\t\t(not Posix)\n"
                    317:                "\tn1 -eq n2\tnumbers n1 and n2 are equal\n"
                    318:                "\tn1 -ge n2\tnumber n1 is greater than or equal to n2\n"
                    319:                "\tn1 -gt n2\tnumber n1 is greater than n2\n"
                    320:                "\tn1 -le n2\tnumber n1 is less than or equal to n2\n"
                    321:                "\tn1 -lt n2\tnumber n1 is less than n2\n"
                    322:                "\tn1 -ne n2\tnumbers n1 and n2 are not equal\n"
                    323:                "\tfile1 -nt file2\tfile1 is newer than file2\t\t(not Posix)\n"
                    324:                "\tfile1 -ot file2\tfile1 is older than file2\t\t(not Posix)\n"
                    325:                );
                    326:        fprintf(stderr,
                    327:                "Expression grouping:\n"
                    328:                "\t! exp\t\texp is false\n"
                    329:                "\texp1 -a exp2\texp1 and exp2 are true\t\t\t(not Posix)\n"
                    330:                "\texp1 -o exp2\texp1 or exp2 is true\t\t\t(not Posix)\n"
                    331:                "\t( exp )\t\tparentheses for grouping\t\t(not Posix)\n"
                    332:                );
                    333: #endif
                    334:        exit(ERROR);
                    335: }
                    336: 
                    337: /* Elementary test routines; each returns 1 if true, 0 if false. */
                    338: /* File exists and is a block special file. */
                    339: int
                    340: x_b(s) char *s;
                    341: {
                    342:        return stat(s, &sb)>=0 && (sb.st_mode&S_IFMT)==S_IFBLK;
                    343: }
                    344: 
                    345: /* File exists and is a character special file. */
                    346: int
                    347: x_c(s) char *s;
                    348: {
                    349:        return stat(s, &sb)>=0 && (sb.st_mode&S_IFMT)==S_IFCHR;
                    350: }
                    351: 
                    352: /* File exists and is a directory. */
                    353: int
                    354: x_d(s) char *s;
                    355: {
                    356:        return stat(s, &sb)>=0 && (sb.st_mode&S_IFMT)==S_IFDIR;
                    357: }
                    358: 
                    359: /* File exists. */
                    360: int
                    361: x_e(s) char *s;
                    362: {
                    363:        return stat(s, &sb)>=0;
                    364: }
                    365: 
                    366: /* Two files are the same. */
                    367: int
                    368: x_ef(s1, s2) char *s1, *s2;
                    369: {
                    370:        return stat(s1, &sb)>=0
                    371:            && stat(s2, &sb2)>=0
                    372:            && sb.st_dev==sb2.st_dev && sb.st_ino==sb2.st_ino;
                    373: }
                    374: 
                    375: /* Two numbers are equal. */
                    376: int
                    377: x_eq(s1, s2) char *s1, *s2;
                    378: {
                    379:        return atol(s1) == atol(s2);
                    380: }
                    381: 
                    382: /* File exists and is an ordinary file. */
                    383: /* This is POSIX semantics; BSD uses "file exists and is not a directory." */
                    384: int
                    385: x_f(s) char *s;
                    386: {
                    387:        return stat(s, &sb)>=0 && (sb.st_mode&S_IFMT)==S_IFREG;
                    388: }
                    389: 
                    390: /* File exists and is setgid. */
                    391: int
                    392: x_g(s) char *s;
                    393: {
                    394:        return stat(s, &sb)>=0 && (sb.st_mode&S_ISGID)!=0;
                    395: }
                    396: 
                    397: /* First number is greater than or equal to the second. */
                    398: int
                    399: x_ge(s1, s2) char *s1, *s2;
                    400: {
                    401:        return atol(s1) >= atol(s2);
                    402: }
                    403: 
                    404: /* First number is greater than the second. */
                    405: int
                    406: x_gt(s1, s2) char *s1, *s2;
                    407: {
                    408:        return atol(s1) > atol(s2);
                    409: }
                    410: 
                    411: /* File exists and has sticky bit set. */
                    412: int
                    413: x_k(s) char *s;
                    414: {
                    415:        return stat(s, &sb)>=0 && (sb.st_mode&S_ISVTX)!=0;
                    416: }
                    417: 
                    418: /* File is a link. */
                    419: int
                    420: x_L(s) char *s;
                    421: {
                    422:        return stat(s, &sb)>=0 && sb.st_nlink>1;
                    423: }
                    424: 
                    425: /* First number is less than or equal to the second. */
                    426: int
                    427: x_le(s1, s2) char *s1, *s2;
                    428: {
                    429:        return atol(s1) <= atol(s2);
                    430: }
                    431: 
                    432: /* First number is less than the second. */
                    433: int
                    434: x_lt(s1, s2) char *s1, *s2;
                    435: {
                    436:        return atol(s1) < atol(s2);
                    437: }
                    438: 
                    439: /* String length is non-zero. */
                    440: int
                    441: x_n(s) char *s;
                    442: {
                    443:        return s[0] != '\0';
                    444: }
                    445: 
                    446: /* Two numbers are not equal. */
                    447: int
                    448: x_ne(s1, s2) char *s1, *s2;
                    449: {
                    450:        return atol(s1) != atol(s2);
                    451: }
                    452: 
                    453: /* First file is newer than the second. */
                    454: int
                    455: x_nt(s1, s2) char *s1, *s2;
                    456: {
                    457:        return stat(s1, &sb)>=0
                    458:            && stat(s2, &sb2)>=0
                    459:            && sb.st_mtime>sb2.st_mtime;
                    460: }
                    461: 
                    462: /* First file is older than the second. */
                    463: int
                    464: x_ot(s1, s2) char *s1, *s2;
                    465: {
                    466:        return stat(s1, &sb)>=0
                    467:            && stat(s2, &sb2)>=0
                    468:            && sb.st_mtime<sb2.st_mtime;
                    469: }
                    470: 
                    471: /* File exists and is a named pipe. */
                    472: int
                    473: x_p(s) char *s;
                    474: {
                    475:        return stat(s, &sb)>=0 && (sb.st_mode&S_IFMT)==S_IFPIP;
                    476: }
                    477: 
                    478: /* File exists and is readable. */
                    479: int
                    480: x_r(s) char *s;
                    481: {
                    482:        return access(s, AREAD) >= 0;
                    483: }
                    484: 
                    485: /* File exists and has a non-zero size. */
                    486: int
                    487: x_s(s) char *s;
                    488: {
                    489:        return stat(s, &sb)>=0 && sb.st_size>0;
                    490: }
                    491: 
                    492: /* Two strings are lexicographically equal. */
                    493: int
                    494: x_str_eq(s1, s2) char *s1, *s2;
                    495: {
                    496:        return equal(s1, s2);
                    497: }
                    498: 
                    499: /* First string is lexicographically less than second. */
                    500: int
                    501: x_str_lt(s1, s2) char *s1, *s2;
                    502: {
                    503:        return strcmp(s1, s2) < 0;
                    504: }
                    505: 
                    506: /* First string is lexicographically greater than second. */
                    507: int
                    508: x_str_gt(s1, s2) char *s1, *s2;
                    509: {
                    510:        return strcmp(s1, s2) > 0;
                    511: }
                    512: 
                    513: /* Two strings are lexicographically unequal. */
                    514: int
                    515: x_str_ne(s1, s2) char *s1, *s2;
                    516: {
                    517:        return !equal(s1, s2);
                    518: }
                    519: 
                    520: /* File descriptor is associated with a terminal. */
                    521: /* Posix says the fd in "-t fd" arg is required, not optional. */
                    522: int
                    523: x_t(s) char *s;
                    524: {
                    525:        return isatty(atoi(s));
                    526: }
                    527: 
                    528: /* File exists and is setuid. */
                    529: int
                    530: x_u(s) char *s;
                    531: {
                    532:        return stat(s, &sb)>=0 && (sb.st_mode&S_ISUID)!=0;
                    533: }
                    534: 
                    535: /* File exists and is writeable. */
                    536: int
                    537: x_w(s) char *s;
                    538: {
                    539:        return access(s, AWRITE) >= 0;
                    540: }
                    541: 
                    542: /* File exists and is executable. */
                    543: int
                    544: x_x(s) char *s;
                    545: {
                    546:        return access(s, AEXEC) >= 0;
                    547: }
                    548: 
                    549: /* String length is zero. */
                    550: int
                    551: x_z(s) char *s;
                    552: {
                    553:        return s[0] == '\0';
                    554: }
                    555: 
                    556: /* end of cmd/test.c */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.