Annotation of coherent/b/bin/test.c.steve, revision 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.