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