diff options
Diffstat (limited to 'stest.c')
| -rw-r--r-- | stest.c | 85 |
1 files changed, 85 insertions, 0 deletions
| @@ -0,0 +1,85 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | #include <stdbool.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include <stdlib.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <unistd.h> | ||
| 7 | #include <sys/stat.h> | ||
| 8 | |||
| 9 | #define OPER(x) (oper[(x)-'a']) | ||
| 10 | |||
| 11 | static bool test(const char *); | ||
| 12 | |||
| 13 | static bool quiet = false; | ||
| 14 | static bool oper[26]; | ||
| 15 | static struct stat old, new; | ||
| 16 | |||
| 17 | int | ||
| 18 | main(int argc, char *argv[]) { | ||
| 19 | char buf[BUFSIZ], *p; | ||
| 20 | bool match = false; | ||
| 21 | int opt; | ||
| 22 | |||
| 23 | while((opt = getopt(argc, argv, "C:bcdefghn:o:pqrsuwx")) != -1) | ||
| 24 | switch(opt) { | ||
| 25 | case 'C': /* tests relative to directory */ | ||
| 26 | if(chdir(optarg) == -1) { | ||
| 27 | perror(optarg); | ||
| 28 | exit(2); | ||
| 29 | } | ||
| 30 | break; | ||
| 31 | case 'n': /* newer than file */ | ||
| 32 | case 'o': /* older than file */ | ||
| 33 | if(!(OPER(opt) = stat(optarg, (opt == 'n' ? &new : &old)) == 0)) | ||
| 34 | perror(optarg); | ||
| 35 | break; | ||
| 36 | case 'q': /* quiet (no output, just status) */ | ||
| 37 | quiet = true; | ||
| 38 | break; | ||
| 39 | default: /* miscellaneous operators */ | ||
| 40 | OPER(opt) = true; | ||
| 41 | break; | ||
| 42 | case '?': /* error: unknown flag */ | ||
| 43 | fprintf(stderr, "usage: %s [-bcdefghpqrsuwx] [-C dir] [-n file] [-o file] [file...]\n", argv[0]); | ||
| 44 | exit(2); | ||
| 45 | } | ||
| 46 | if(optind == argc) | ||
| 47 | while(fgets(buf, sizeof buf, stdin)) { | ||
| 48 | if(*(p = &buf[strlen(buf)-1]) == '\n') | ||
| 49 | *p = '\0'; | ||
| 50 | match |= test(buf); | ||
| 51 | } | ||
| 52 | else | ||
| 53 | while(optind < argc) | ||
| 54 | match |= test(argv[optind++]); | ||
| 55 | |||
| 56 | return match ? 0 : 1; | ||
| 57 | } | ||
| 58 | |||
| 59 | bool | ||
| 60 | test(const char *path) { | ||
| 61 | struct stat st; | ||
| 62 | |||
| 63 | if((!OPER('b') || (stat(path, &st) == 0 && S_ISBLK(st.st_mode))) /* block special */ | ||
| 64 | && (!OPER('c') || (stat(path, &st) == 0 && S_ISCHR(st.st_mode))) /* character special */ | ||
| 65 | && (!OPER('d') || (stat(path, &st) == 0 && S_ISDIR(st.st_mode))) /* directory */ | ||
| 66 | && (!OPER('e') || (access(path, F_OK) == 0)) /* exists */ | ||
| 67 | && (!OPER('f') || (stat(path, &st) == 0 && S_ISREG(st.st_mode))) /* regular file */ | ||
| 68 | && (!OPER('g') || (stat(path, &st) == 0 && (st.st_mode & S_ISGID))) /* set-group-id flag */ | ||
| 69 | && (!OPER('h') || (lstat(path, &st) == 0 && S_ISLNK(st.st_mode))) /* symbolic link */ | ||
| 70 | && (!OPER('n') || (stat(path, &st) == 0 && st.st_mtime > new.st_mtime)) /* newer than file */ | ||
| 71 | && (!OPER('o') || (stat(path, &st) == 0 && st.st_mtime < old.st_mtime)) /* older than file */ | ||
| 72 | && (!OPER('p') || (stat(path, &st) == 0 && S_ISFIFO(st.st_mode))) /* named pipe */ | ||
| 73 | && (!OPER('r') || (access(path, R_OK) == 0)) /* readable */ | ||
| 74 | && (!OPER('s') || (stat(path, &st) == 0 && st.st_size > 0)) /* not empty */ | ||
| 75 | && (!OPER('u') || (stat(path, &st) == 0 && (st.st_mode & S_ISUID))) /* set-user-id flag */ | ||
| 76 | && (!OPER('w') || (access(path, W_OK) == 0)) /* writable */ | ||
| 77 | && (!OPER('x') || (access(path, X_OK) == 0))) { /* executable */ | ||
| 78 | if(quiet) | ||
| 79 | exit(0); | ||
| 80 | puts(path); | ||
| 81 | return true; | ||
| 82 | } | ||
| 83 | else | ||
| 84 | return false; | ||
| 85 | } | ||
