1 /*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 *
4 * HDF is dual licensed: you can use it either under the terms of
5 * the GPL, or the BSD license, at your option.
6 * See the LICENSE file in the root of this repository for complete details.
7 */
8
9 #include "util/file.h"
10
11 #include <climits>
12 #include <cstdlib>
13 #include <cstring>
14 #include <dirent.h>
15 #include <functional>
16 #include <string>
17 #include <algorithm>
18 #include <queue>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22
23 #include "util/common.h"
24 #include "util/logger.h"
25 #include "util/string_helper.h"
26 #include "util/string_builder.h"
27
28 namespace OHOS {
29 namespace HDI {
File(const std::string & path,unsigned int mode)30 File::File(const std::string &path, unsigned int mode) : mode_(mode)
31 {
32 if (path.empty()) {
33 return;
34 }
35
36 if ((mode_ & READ) != 0) {
37 OpenByRead(path);
38 return;
39 }
40
41 if ((mode_ & WRITE) != 0) {
42 fd_ = fopen(path.c_str(), "w+");
43 } else if ((mode_ & APPEND) != 0) {
44 fd_ = fopen(path.c_str(), "a+");
45 }
46
47 if (fd_ == nullptr) {
48 Logger::E(TAG, "can't open '%s'", path.c_str());
49 return;
50 }
51
52 path_ = RealPath(path);
53 }
54
~File()55 File::~File()
56 {
57 Close();
58 }
59
OpenByRead(const std::string & path)60 void File::OpenByRead(const std::string &path)
61 {
62 if (!CheckValid(path)) {
63 Logger::E(TAG, "failed to check path '%s'", path.c_str());
64 return;
65 }
66
67 std::string realPath = RealPath(path);
68 if (realPath.empty()) {
69 Logger::E(TAG, "invalid path '%s'", path.c_str());
70 return;
71 }
72
73 fd_ = fopen(realPath.c_str(), "r");
74 if (fd_ == nullptr) {
75 Logger::E(TAG, "can't open '%s'", realPath.c_str());
76 return;
77 }
78
79 path_ = realPath;
80 PeekChar();
81 }
82
GetChar()83 char File::GetChar()
84 {
85 char c = PeekChar();
86
87 if (position_ + 1 <= size_) {
88 position_++;
89
90 if (c != '\n') {
91 columnNo_++;
92 } else {
93 columnNo_ = 1;
94 lineNo_++;
95 }
96 }
97 return c;
98 }
99
PeekChar()100 char File::PeekChar()
101 {
102 if (position_ + 1 > size_) {
103 size_t size = Read();
104 if (size == 0) {
105 isEof_ = true;
106 }
107 }
108
109 return buffer_[position_];
110 }
111
IsEof() const112 bool File::IsEof() const
113 {
114 return isEof_ || buffer_[position_] == -1;
115 }
116
Read()117 size_t File::Read()
118 {
119 if (isEof_ || isError_) {
120 return -1;
121 }
122
123 std::fill(buffer_, buffer_ + BUFFER_SIZE, 0);
124 size_t count = fread(buffer_, 1, BUFFER_SIZE - 1, fd_);
125 if (count < BUFFER_SIZE - 1) {
126 isError_ = ferror(fd_) != 0;
127 buffer_[count] = -1;
128 }
129 size_ = count;
130 position_ = 0;
131 return count;
132 }
133
ReadData(void * data,size_t size) const134 size_t File::ReadData(void *data, size_t size) const
135 {
136 if (data == nullptr || size == 0) {
137 return 0;
138 }
139
140 if (fd_ == nullptr) {
141 return 0;
142 }
143
144 return fread(data, 1, size, fd_);
145 }
146
WriteData(const void * data,size_t size) const147 bool File::WriteData(const void *data, size_t size) const
148 {
149 if (data == nullptr || size == 0) {
150 return true;
151 }
152
153 if (fd_ == nullptr || !(mode_ & (WRITE | APPEND))) {
154 return false;
155 }
156
157 size_t count = fwrite(data, size, 1, fd_);
158 return count == 1;
159 }
160
Flush() const161 void File::Flush() const
162 {
163 if ((mode_ & (WRITE | APPEND)) && fd_ != nullptr) {
164 fflush(fd_);
165 }
166 }
167
Reset() const168 bool File::Reset() const
169 {
170 if (fd_ == nullptr) {
171 return false;
172 }
173
174 return fseek(fd_, 0, SEEK_SET) == 0;
175 }
176
Skip(long size) const177 bool File::Skip(long size) const
178 {
179 if (fd_ == nullptr) {
180 return false;
181 }
182
183 return fseek(fd_, size, SEEK_CUR) == 0;
184 }
185
Close()186 void File::Close()
187 {
188 if (fd_ != nullptr) {
189 fclose(fd_);
190 fd_ = nullptr;
191 }
192 }
193
CreateParentDir(const std::string & path)194 bool File::CreateParentDir(const std::string &path)
195 {
196 if (access(path.c_str(), F_OK | R_OK | W_OK) == 0) {
197 return true;
198 }
199
200 size_t pos = 1;
201 while ((pos = path.find(SEPARATOR, pos)) != std::string::npos) {
202 std::string partPath = StringHelper::SubStr(path, 0, pos);
203 struct stat st;
204 if (stat(partPath.c_str(), &st) < 0) {
205 if (errno != ENOENT) {
206 return false;
207 }
208
209 #ifndef __MINGW32__
210 if (mkdir(partPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
211 #else
212 if (mkdir(partPath.c_str()) < 0) {
213 #endif
214 return false;
215 }
216 } else if (!S_ISDIR(st.st_mode)) {
217 return false;
218 }
219 pos += 1;
220 }
221 return true;
222 }
223
224 std::string File::AdapterPath(const std::string &path)
225 {
226 #ifndef __MINGW32__
227 std::string newPath = StringHelper::Replace(path, '\\', '/');
228 #else
229 std::string newPath = StringHelper::Replace(path, '/', '\\');
230 #endif
231
232 // "foo/v1_0//ifoo.h" -> "foo/v1_0/ifoo.h"
233 StringBuilder adapterPath;
234 bool hasSep = false;
235 for (size_t i = 0; i < newPath.size(); i++) {
236 char c = newPath[i];
237 if (c == SEPARATOR) {
238 if (hasSep) {
239 continue;
240 }
241 adapterPath.Append(c);
242 hasSep = true;
243 } else {
244 adapterPath.Append(c);
245 hasSep = false;
246 }
247 }
248 return adapterPath.ToString();
249 }
250
251 std::string File::AdapterRealPath(const std::string &path)
252 {
253 if (path.empty()) {
254 return "";
255 }
256 return RealPath(File::AdapterPath(path));
257 }
258
259 std::string File::RealPath(const std::string &path)
260 {
261 if (path.empty()) {
262 return "";
263 }
264
265 char realPath[PATH_MAX + 1];
266 #ifdef __MINGW32__
267 char *absPath = _fullpath(realPath, path.c_str(), PATH_MAX);
268 #else
269 char *absPath = realpath(path.c_str(), realPath);
270 #endif
271 return absPath == nullptr ? "" : absPath;
272 }
273
274 bool File::CheckValid(const std::string &path)
275 {
276 if (access(path.c_str(), F_OK | R_OK | W_OK) != 0) {
277 return false;
278 }
279
280 struct stat st;
281 if (stat(path.c_str(), &st) < 0) {
282 return false;
283 }
284
285 if (S_ISDIR(st.st_mode)) {
286 return false;
287 }
288
289 return true;
290 }
291
292 std::set<std::string> File::FindFiles(const std::string &rootDir)
293 {
294 if (rootDir.empty()) {
295 return std::set<std::string>();
296 }
297
298 std::set<std::string> files;
299 std::queue<std::string> dirs;
300 dirs.push(rootDir);
301 while (!dirs.empty()) {
302 std::string dirPath = dirs.front().back() == SEPARATOR ? dirs.front() : dirs.front() + SEPARATOR;
303 dirs.pop();
304 DIR *dir = opendir(dirPath.c_str());
305 if (dir == nullptr) {
306 Logger::E(TAG, "failed to open '%s', errno:%d", dirPath.c_str(), errno);
307 continue;
308 }
309
310 struct dirent *dirInfo = readdir(dir);
311 for (; dirInfo != nullptr; dirInfo = readdir(dir)) {
312 if (strcmp(dirInfo->d_name, ".") == 0 || strcmp(dirInfo->d_name, "..") == 0) {
313 continue;
314 }
315
316 if (dirInfo->d_type == DT_REG && StringHelper::EndWith(dirInfo->d_name, ".idl")) {
317 std::string filePath = dirPath + dirInfo->d_name;
318 files.insert(filePath);
319 continue;
320 }
321
322 if (dirInfo->d_type == DT_DIR) {
323 dirs.emplace(dirPath + dirInfo->d_name);
324 continue;
325 }
326 }
327 closedir(dir);
328 }
329
330 return files;
331 }
332
333 size_t File::GetHashKey()
334 {
335 StringBuilder fileStr;
336 while (!IsEof()) {
337 fileStr.Append(GetChar());
338 }
339
340 return std::hash<std::string>()(fileStr.ToString());
341 }
342 } // namespace HDI
343 } // namespace OHOS