op_file.c revision 7984f7ab3e13cda0c3b04ffeb2608f232e57f93a
1/** 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); 97 if (stat(name, st_buf) != 0) { 98 free(name); 99 name = NULL; 100 } 101 return name; 102} 103 104 105int get_matching_pathnames(void * name_list, get_pathname_callback getpathname, 106 char const * base_dir, char const * filter, 107 enum recursion_type recursion) 108{ 109/* The algorithm below depends on recursion type (of which there are 3) 110 * and whether the current dirent matches the filter. There are 6 possible 111 * different behaviors, which is why we define 6 case below in the switch 112 * statement of the algorithm. Actually, when the recursion type is 113 * MATCH_DIR_ONLY_RECURSION, the behavior is the same, whether or not the dir 114 * entry matches the filter. However, the behavior of the recursion types 115 * NO_RECURSION and MATCH_ANY_ENTRY_RECURSION do depend on the dir entry 116 * filter match, so for simplicity, we perform this match for all recursion 117 * types and logically OR the match result with the value of the passed 118 * recursion_type. 119 */ 120#define NO_MATCH 0 121#define MATCH 1 122 123 DIR * dir; 124 struct dirent * ent; 125 struct stat stat_buffer; 126 int match; 127 char * name = NULL; 128 129 if (!(dir = opendir(base_dir))) 130 return -1; 131 while ((ent = readdir(dir)) != 0) { 132 if (is_dot_or_dotdot(ent->d_name)) 133 continue; 134 if (fnmatch(filter, ent->d_name, 0) == 0) 135 match = 1; 136 else 137 match = 0; 138 139 switch (recursion | match) { 140 case NO_RECURSION + NO_MATCH: 141 case MATCH_ANY_ENTRY_RECURSION + NO_MATCH: 142 // nothing to do but continue the loop 143 break; 144 case NO_RECURSION + MATCH: 145 getpathname(ent->d_name, name_list); 146 break; 147 case MATCH_ANY_ENTRY_RECURSION + MATCH: 148 name = make_pathname_from_dirent(base_dir, ent, 149 &stat_buffer); 150 if (name && S_ISDIR(stat_buffer.st_mode) && 151 !S_ISLNK(stat_buffer.st_mode)) { 152 get_matching_pathnames( 153 name_list, getpathname, 154 name, filter, recursion); 155 } else { 156 getpathname(name, name_list); 157 } 158 free(name); 159 break; 160 case MATCH_DIR_ONLY_RECURSION + NO_MATCH: 161 case MATCH_DIR_ONLY_RECURSION + MATCH: 162 name = make_pathname_from_dirent(base_dir, ent, 163 &stat_buffer); 164 if (name && S_ISDIR(stat_buffer.st_mode) && 165 !S_ISLNK(stat_buffer.st_mode)) { 166 /* Check if full directory name contains 167 * match to the filter; if so, add it to 168 * name_list and quit; else, recurse. 169 */ 170 if (!fnmatch(filter, name, 0)) { 171 getpathname(name, name_list); 172 } else { 173 get_matching_pathnames( 174 name_list, getpathname, 175 name, filter, recursion); 176 } 177 } 178 free(name); 179 break; 180 } 181 } 182 closedir(dir); 183 184 return 0; 185} 186