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
30using namespace std;
31
32
33bool 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
79bool 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
86bool 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
100string 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
109bool op_file_readable(string const & file)
110{
111	return op_file_readable(file.c_str());
112}
113
114static 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
120bool 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 */
136static 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
149string 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
172string 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