1 /**
2 * @file file_manip.cpp
3 * Useful file management helpers
4 *
5 * @remark Copyright 2002 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author Philippe Elie
9 * @author John Levon
10 */
11
12 #include <unistd.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <utime.h>
16 #include <limits.h>
17 #include <stdlib.h>
18
19 #include <cstdio>
20 #include <cerrno>
21 #include <iostream>
22 #include <fstream>
23 #include <vector>
24
25 #include "op_file.h"
26
27 #include "file_manip.h"
28 #include "string_manip.h"
29
30 using namespace std;
31
32
copy_file(string const & source,string const & destination)33 bool copy_file(string const & source, string const & destination)
34 {
35 int retval;
36 struct stat buf;
37 if (stat(source.c_str(), &buf))
38 return false;
39
40 if (!op_file_readable(source))
41 return false;
42
43 ifstream in(source.c_str());
44 if (!in)
45 return false;
46
47 mode_t mode = buf.st_mode & ~S_IFMT;
48 if (!(mode & S_IWUSR))
49 mode |= S_IWUSR;
50
51 int fd = open(destination.c_str(), O_RDWR|O_CREAT, mode);
52 if (fd < 0)
53 return false;
54 close(fd);
55
56
57 // ignore error here: a simple user can copy a root.root 744 file
58 // but can't chown the copied file to root.
59 retval = chown(destination.c_str(), buf.st_uid, buf.st_gid);
60
61 // a scope to ensure out is closed before changing is mtime/atime
62 {
63 ofstream out(destination.c_str(), ios::trunc);
64 if (!out)
65 return false;
66 out << in.rdbuf();
67 }
68
69 struct utimbuf utim;
70 utim.actime = buf.st_atime;
71 utim.modtime = buf.st_mtime;
72 if (utime(destination.c_str(), &utim))
73 return false;
74
75 return true;
76 }
77
78
is_directory(string const & dirname)79 bool is_directory(string const & dirname)
80 {
81 struct stat st;
82 return !stat(dirname.c_str(), &st) && S_ISDIR(st.st_mode);
83 }
84
85
is_files_identical(string const & file1,string const & file2)86 bool is_files_identical(string const & file1, string const & file2)
87 {
88 struct stat st1;
89 struct stat st2;
90
91 if (stat(file1.c_str(), &st1) == 0 && stat(file2.c_str(), &st2) == 0) {
92 if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
93 return true;
94 }
95
96 return false;
97 }
98
99
op_realpath(string const & name)100 string const op_realpath(string const & name)
101 {
102 static char tmp[PATH_MAX];
103 if (!realpath(name.c_str(), tmp))
104 return name;
105 return string(tmp);
106 }
107
108
op_file_readable(string const & file)109 bool op_file_readable(string const & file)
110 {
111 return op_file_readable(file.c_str());
112 }
113
get_pathname(const char * pathname,void * name_list)114 static void get_pathname(const char * pathname, void * name_list)
115 {
116 list<string> * file_list = (list<string> *)name_list;
117 file_list->push_back(pathname);
118 }
119
create_file_list(list<string> & file_list,string const & base_dir,string const & filter,bool recursive)120 bool create_file_list(list<string> & file_list, string const & base_dir,
121 string const & filter, bool recursive)
122 {
123 return !get_matching_pathnames(&file_list, get_pathname,
124 base_dir.c_str(), filter.c_str(),
125 recursive ? MATCH_ANY_ENTRY_RECURSION :
126 NO_RECURSION) ? true : false;
127
128 }
129
130
131 /**
132 * @param path_name the path where we remove trailing '/'
133 *
134 * erase all trailing '/' in path_name except if the last '/' is at pos 0
135 */
erase_trailing_path_separator(string const & path_name)136 static string erase_trailing_path_separator(string const & path_name)
137 {
138 string result(path_name);
139
140 while (result.length() > 1) {
141 if (result[result.length() - 1] != '/')
142 break;
143 result.erase(result.length() - 1, 1);
144 }
145
146 return result;
147 }
148
op_dirname(string const & file_name)149 string op_dirname(string const & file_name)
150 {
151 string result = erase_trailing_path_separator(file_name);
152 if (result.find_first_of('/') == string::npos)
153 return ".";
154
155 // catch result == "/"
156 if (result.length() == 1)
157 return result;
158
159 size_t pos = result.find_last_of('/');
160
161 // "/usr" must return "/"
162 if (pos == 0)
163 pos = 1;
164
165 result.erase(pos, result.length() - pos);
166
167 // "////usr" must return "/"
168 return erase_trailing_path_separator(result);
169 }
170
171
op_basename(string const & path_name)172 string op_basename(string const & path_name)
173 {
174 string result = erase_trailing_path_separator(path_name);
175
176 // catch result == "/"
177 if (result.length() == 1)
178 return result;
179
180 return erase_to_last_of(result, '/');
181 }
182