]>
Commit | Line | Data |
---|---|---|
974e3884 A |
1 | #include <err.h> |
2 | #include <stdbool.h> | |
3 | #include <stdio.h> | |
4 | #include <stdlib.h> | |
5 | #include <string.h> | |
6 | #include <sysexits.h> | |
7 | #include <unistd.h> | |
8 | #include <fts.h> | |
9 | #include <sys/types.h> | |
10 | #include <sys/stat.h> | |
11 | ||
12 | // Can ASan fts by uncommenting below | |
13 | //#include "../gen/fts.c" | |
14 | ||
15 | #ifndef DARWINTEST | |
16 | #define fts_find_main main | |
17 | #else | |
18 | #include <darwintest.h> | |
19 | #endif | |
20 | ||
21 | int fts_find_main(int argc, char *argv[]); | |
22 | ||
b061a43b | 23 | #ifndef DARWINTEST |
974e3884 A |
24 | static char * |
25 | stat_str(struct stat *st) | |
26 | { | |
27 | static char charbuf[256]; | |
28 | snprintf(charbuf, sizeof(charbuf), "dev: %d, mode: %x, nlink: %d, ino: %lld, " | |
29 | "owner: %d/%d, rdev: %d, mtime: %ld, ctime: %ld, btime: %ld, " | |
30 | "size: %lld, blocks: %lld, blksize: %d, flags: %d, gen: %d", | |
31 | st->st_dev, st->st_mode, st->st_nlink, st->st_ino, st->st_uid, | |
32 | st->st_gid, st->st_rdev, st->st_mtimespec.tv_sec, | |
33 | st->st_ctimespec.tv_sec, st->st_birthtimespec.tv_sec, st->st_size, | |
34 | st->st_blocks, st->st_blksize, st->st_flags, st->st_gen); | |
35 | return charbuf; | |
36 | } | |
b061a43b | 37 | #endif // DARWINTEST |
974e3884 A |
38 | |
39 | int | |
40 | fts_find_main(int argc, char *argv[]) | |
41 | { | |
42 | FTS *fts; | |
43 | FTSENT *ftse; | |
44 | ||
45 | bool print_children = false; | |
46 | int fts_options = FTS_COMFOLLOW | FTS_XDEV; | |
b061a43b A |
47 | optind = 1; |
48 | optreset = 1; | |
974e3884 A |
49 | |
50 | int ch; | |
51 | while ((ch = getopt(argc, argv, "lpcdsS")) != -1){ | |
52 | switch (ch){ | |
53 | case 'l': | |
54 | fts_options |= FTS_LOGICAL; | |
55 | break; | |
56 | case 'p': | |
57 | fts_options |= FTS_PHYSICAL; | |
58 | break; | |
59 | case 'c': | |
60 | print_children = true; | |
61 | break; | |
62 | case 'd': | |
63 | fts_options |= FTS_NOCHDIR; | |
64 | break; | |
65 | case 's': | |
66 | fts_options |= FTS_NOSTAT; | |
67 | break; | |
68 | case 'S': | |
69 | fts_options |= FTS_NOSTAT_TYPE; | |
70 | break; | |
71 | case '?': | |
72 | fprintf(stderr, "Usage: %s (-l|-p) [-c] [-d] [-s|-S] <path> ...\n", argv[0]); | |
73 | exit(EX_USAGE); | |
74 | } | |
75 | } | |
76 | ||
77 | if ((fts_options & (FTS_LOGICAL|FTS_PHYSICAL)) == 0){ | |
78 | fprintf(stderr, "Usage: %s (-l|-p) [-c] [-s|-S] <path> ...\n", argv[0]); | |
79 | exit(EX_USAGE); | |
80 | } | |
81 | ||
82 | argc -= optind; | |
83 | argv += optind; | |
84 | ||
85 | char **args = alloca((size_t)(argc + 1)*sizeof(char*)); | |
86 | for (int i = 0; i < argc; i++){ | |
87 | args[i] = argv[i]; | |
88 | } | |
89 | args[argc] = NULL; | |
90 | fts = fts_open_b(args, fts_options, ^(const FTSENT **a, const FTSENT **b){ | |
91 | return strcmp((*a)->fts_name, (*b)->fts_name); | |
92 | }); | |
93 | if (!fts) err(EX_DATAERR, "fts_open_b"); | |
94 | ||
95 | while ((ftse = fts_read(fts)) != NULL) { | |
96 | #ifndef DARWINTEST | |
97 | if (!print_children || (ftse->fts_info & FTS_D)){ | |
98 | printf("%s (%s): 0x%x\n", ftse->fts_path, ftse->fts_name, ftse->fts_info); | |
99 | if (!(fts_options & (FTS_NOSTAT|FTS_NOSTAT_TYPE))) printf("\t\t%s\n", stat_str(ftse->fts_statp)); | |
100 | } | |
b061a43b | 101 | #endif // DARWINTEST |
974e3884 A |
102 | if (print_children){ |
103 | FTSENT *child = fts_children(fts, 0); | |
104 | while (child){ | |
105 | #ifndef DARWINTEST | |
106 | if (child->fts_info & FTS_F){ | |
107 | printf("\t%s (%s): 0x%x\n", child->fts_path, child->fts_name, child->fts_info); | |
108 | if (!(fts_options & (FTS_NOSTAT|FTS_NOSTAT_TYPE))) printf("\t\t%s\n", stat_str(child->fts_statp)); | |
109 | } | |
b061a43b | 110 | #endif // DARWINTEST |
974e3884 A |
111 | child = child->fts_link; |
112 | } | |
113 | } | |
114 | } | |
115 | ||
116 | (void)fts_close(fts); | |
117 | return 0; | |
118 | } | |
119 | ||
120 | #ifdef DARWINTEST | |
121 | T_DECL(fts_find, "A find(1) example in fts"){ | |
122 | int fts_argc = 3; | |
123 | char *fts_argv[] = {"fts_find", "-lc", "/System", NULL}; | |
124 | if (fts_find_main(fts_argc, fts_argv) == 0){ | |
125 | T_PASS("fts_find() completed successfully"); | |
126 | } else { | |
127 | T_FAIL("fts_find() exited with error"); | |
128 | } | |
129 | } | |
b061a43b A |
130 | |
131 | T_DECL(fts_find_empty_path, "Test result for empty path"){ | |
132 | char *paths[] = {"/System", "", NULL}; | |
133 | ||
134 | FTS *fts = fts_open_b(paths, 0, ^(const FTSENT **a, const FTSENT **b){ | |
135 | return strcmp((*a)->fts_name, (*b)->fts_name); | |
136 | }); | |
137 | if (fts == NULL) { | |
138 | T_FAIL("fts_open() failed"); | |
139 | return; | |
140 | } | |
141 | ||
142 | // The first entry name should be the empty string, because of the sort | |
143 | // order. The second entry should be "System". | |
144 | FTSENT *entry = fts_read(fts); | |
145 | T_ASSERT_NOTNULL(entry, "First fts_read() returned NULL"); | |
146 | T_ASSERT_EQ_STR(entry->fts_name, "", "First entry name is empty"); | |
147 | T_ASSERT_EQ((int)entry->fts_info, FTS_NS, "First fts_info is FTS_NS"); | |
148 | T_ASSERT_EQ(entry->fts_errno, ENOENT, "First fts_errno is ENOENT"); | |
149 | ||
150 | entry = fts_read(fts); | |
151 | T_ASSERT_NOTNULL(entry, "Second fts_read() returned NULL"); | |
152 | T_ASSERT_EQ_STR(entry->fts_name, "System", "Second entry name is System"); | |
153 | T_ASSERT_EQ((int)entry->fts_info, FTS_D, "Second fts_info is FTS_D"); | |
154 | T_ASSERT_EQ(entry->fts_errno, 0, "Second fts_errno is 0"); | |
155 | ||
156 | fts_close(fts); | |
157 | } | |
158 | #endif // DARWINTEST |