/**
* @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;
}