a5596eabbd9bd0376ecc94e1b6b4223e33ce732e
[dmenu.git] / stest.c
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 }