This repository has been archived by the owner on Aug 3, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 36
/
op_file.c
195 lines (169 loc) · 4.36 KB
/
op_file.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
/**
* @file op_file.c
* Useful file management helpers
*
* @remark Copyright 2002 OProfile authors
* @remark Read the file COPYING
*
* @author John Levon
* @author Philippe Elie
*/
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <fnmatch.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include "op_file.h"
#include "op_libiberty.h"
int op_file_readable(char const * file)
{
struct stat st;
return !stat(file, &st) && S_ISREG(st.st_mode) && !access(file, R_OK);
}
time_t op_get_mtime(char const * file)
{
struct stat st;
if (stat(file, &st))
return 0;
return st.st_mtime;
}
int create_dir(char const * dir)
{
if (mkdir(dir, 0755)) {
/* FIXME: Does not verify existing is a dir */
if (errno == EEXIST)
return 0;
return errno;
}
return 0;
}
int create_path(char const * path)
{
int ret = 0;
char * str = xstrdup(path);
char * pos = str[0] == '/' ? str + 1 : str;
for ( ; (pos = strchr(pos, '/')) != NULL; ++pos) {
*pos = '\0';
ret = create_dir(str);
*pos = '/';
if (ret)
break;
}
free(str);
return ret;
}
inline static int is_dot_or_dotdot(char const * name)
{
return name[0] == '.' &&
(name[1] == '\0' ||
(name[1] == '.' && name[2] == '\0'));
}
/* If non-null is returned, the caller is responsible for freeing
* the memory allocated for the return value. */
static char * make_pathname_from_dirent(char const * basedir,
struct dirent * ent,
struct stat * st_buf)
{
int name_len;
char * name;
name_len = strlen(basedir) + strlen("/") + strlen(ent->d_name) + 1;
name = xmalloc(name_len);
sprintf(name, "%s/%s", basedir, ent->d_name);
if (stat(name, st_buf) != 0)
{
struct stat lstat_buf;
int err = errno;
if (lstat(name, &lstat_buf) == 0 &&
S_ISLNK(lstat_buf.st_mode)) {
// dangling symlink -- silently ignore
} else {
fprintf(stderr, "stat failed for %s (%s)\n",
name, strerror(err));
}
free(name);
name = NULL;
}
return name;
}
int get_matching_pathnames(void * name_list, get_pathname_callback getpathname,
char const * base_dir, char const * filter,
enum recursion_type recursion)
{
/* The algorithm below depends on recursion type (of which there are 3)
* and whether the current dirent matches the filter. There are 6 possible
* different behaviors, which is why we define 6 case below in the switch
* statement of the algorithm. Actually, when the recursion type is
* MATCH_DIR_ONLY_RECURSION, the behavior is the same, whether or not the dir
* entry matches the filter. However, the behavior of the recursion types
* NO_RECURSION and MATCH_ANY_ENTRY_RECURSION do depend on the dir entry
* filter match, so for simplicity, we perform this match for all recursion
* types and logically OR the match result with the value of the passed
* recursion_type.
*/
#define NO_MATCH 0
#define MATCH 1
DIR * dir;
struct dirent * ent;
struct stat stat_buffer;
int match;
char * name = NULL;
if (!(dir = opendir(base_dir)))
return -1;
while ((ent = readdir(dir)) != 0) {
if (is_dot_or_dotdot(ent->d_name))
continue;
if (fnmatch(filter, ent->d_name, 0) == 0)
match = 1;
else
match = 0;
switch (recursion | match) {
case NO_RECURSION + NO_MATCH:
case MATCH_ANY_ENTRY_RECURSION + NO_MATCH:
// nothing to do but continue the loop
break;
case NO_RECURSION + MATCH:
getpathname(ent->d_name, name_list);
break;
case MATCH_ANY_ENTRY_RECURSION + MATCH:
name = make_pathname_from_dirent(base_dir, ent,
&stat_buffer);
if (name) {
if (S_ISDIR(stat_buffer.st_mode)) {
get_matching_pathnames(
name_list, getpathname,
name, filter, recursion);
} else {
getpathname(name, name_list);
}
}
free(name);
break;
case MATCH_DIR_ONLY_RECURSION + NO_MATCH:
case MATCH_DIR_ONLY_RECURSION + MATCH:
name = make_pathname_from_dirent(base_dir, ent,
&stat_buffer);
if (name && S_ISDIR(stat_buffer.st_mode)) {
/* Check if full directory name contains
* match to the filter; if so, add it to
* name_list and quit; else, recurse.
*/
if (!fnmatch(filter, name, 0)) {
getpathname(name, name_list);
} else {
get_matching_pathnames(
name_list, getpathname,
name, filter, recursion);
}
}
free(name);
break;
}
}
closedir(dir);
return 0;
}