1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "util/file.h"
17
18 #include <climits>
19 #include <cstdlib>
20 #include <cstring>
21 #include <dirent.h>
22 #include <functional>
23 #include <string>
24 #include <algorithm>
25 #include <queue>
26 #include <unistd.h>
27 #include <sstream>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include "util/common.h"
31 #include "util/logger.h"
32 #include "util/string_helper.h"
33 #include "util/string_builder.h"
34
35 namespace OHOS {
36 namespace Idl {
File(const std::string & path,unsigned int mode)37 File::File(const std::string &path, unsigned int mode) : mode_(mode)
38 {
39 if (path.empty()) {
40 return;
41 }
42
43 if ((mode_ & READ) != 0) {
44 OpenByRead(path);
45 return;
46 }
47
48 if ((mode_ & WRITE) != 0) {
49 fd_ = fopen(path.c_str(), "w+");
50 } else if ((mode_ & APPEND) != 0) {
51 fd_ = fopen(path.c_str(), "a+");
52 }
53
54 if (fd_ == nullptr) {
55 Logger::E(TAG, "can't open '%s'", path.c_str());
56 return;
57 }
58
59 path_ = RealPath(path);
60 }
61
~File()62 File::~File()
63 {
64 Close();
65 }
66
OpenByRead(const std::string & path)67 void File::OpenByRead(const std::string &path)
68 {
69 if (!CheckValid(path)) {
70 Logger::E(TAG, "failed to check path '%s'", path.c_str());
71 return;
72 }
73
74 std::string realPath = RealPath(path);
75 if (realPath.empty()) {
76 Logger::E(TAG, "invalid path '%s'", path.c_str());
77 return;
78 }
79
80 Close();
81 fd_ = fopen(realPath.c_str(), "r");
82 if (fd_ == nullptr) {
83 Logger::E(TAG, "can't open '%s'", realPath.c_str());
84 return;
85 }
86
87 path_ = realPath;
88 PeekChar();
89 }
90
GetChar()91 char File::GetChar()
92 {
93 char c = PeekChar();
94
95 if (position_ + 1 <= size_) {
96 position_++;
97
98 if (c != '\n') {
99 columnNo_++;
100 } else {
101 columnNo_ = 1;
102 lineNo_++;
103 }
104 }
105 return c;
106 }
107
PeekChar()108 char File::PeekChar()
109 {
110 if (position_ + 1 > size_) {
111 size_t size = Read();
112 if (size == 0) {
113 isEof_ = true;
114 }
115 }
116
117 return buffer_[position_];
118 }
119
NextChar()120 char File::NextChar()
121 {
122 if (position_ + 1 >= size_) {
123 size_t size = Read();
124 if (size == 0) {
125 isEof_ = true;
126 return '\0';
127 }
128 }
129
130 return buffer_[position_ + 1];
131 }
132
IsEof() const133 bool File::IsEof() const
134 {
135 return isEof_ || buffer_[position_] == static_cast<char>(-1);
136 }
137
Read()138 size_t File::Read()
139 {
140 if (isEof_ || isError_) {
141 return -1;
142 }
143
144 std::fill(buffer_, buffer_ + BUFFER_SIZE, 0);
145 size_t count = fread(buffer_, 1, BUFFER_SIZE - 1, fd_);
146 if (count < BUFFER_SIZE - 1) {
147 isError_ = ferror(fd_) != 0;
148 buffer_[count] = -1;
149 }
150 size_ = count;
151 position_ = 0;
152 if ((count == 0) || (count >= BUFFER_SIZE)) {
153 return -1;
154 }
155 return count;
156 }
157
ReadData(void * data,size_t size) const158 size_t File::ReadData(void *data, size_t size) const
159 {
160 if (data == nullptr || size == 0) {
161 return 0;
162 }
163
164 if (fd_ == nullptr) {
165 return 0;
166 }
167
168 return fread(data, 1, size, fd_);
169 }
170
WriteData(const void * data,size_t size) const171 bool File::WriteData(const void *data, size_t size) const
172 {
173 if (data == nullptr || size == 0) {
174 return true;
175 }
176
177 if (fd_ == nullptr || !(mode_ & (WRITE | APPEND))) {
178 return false;
179 }
180
181 size_t count = fwrite(data, size, 1, fd_);
182 return count == 1;
183 }
184
Flush() const185 void File::Flush() const
186 {
187 if ((mode_ & (WRITE | APPEND)) && fd_ != nullptr) {
188 fflush(fd_);
189 }
190 }
191
Reset() const192 bool File::Reset() const
193 {
194 if (fd_ == nullptr) {
195 return false;
196 }
197
198 return fseek(fd_, 0, SEEK_SET) == 0;
199 }
200
Skip(long size) const201 bool File::Skip(long size) const
202 {
203 if (fd_ == nullptr) {
204 return false;
205 }
206
207 return fseek(fd_, size, SEEK_CUR) == 0;
208 }
209
Close()210 void File::Close()
211 {
212 if (fd_ != nullptr) {
213 fclose(fd_);
214 fd_ = nullptr;
215 }
216 }
217
CreateParentDir(const std::string & path)218 bool File::CreateParentDir(const std::string &path)
219 {
220 if (access(path.c_str(), F_OK | R_OK | W_OK) == 0) {
221 return true;
222 }
223
224 size_t pos = 1;
225 while ((pos = path.find(SEPARATOR, pos)) != std::string::npos) {
226 std::string partPath = StringHelper::SubStr(path, 0, pos);
227 partPath += SEPARATOR;
228 if (File::CreatePartDir(partPath) == false) {
229 return false;
230 }
231 pos += 1;
232 }
233 return true;
234 }
235
CreatePartDir(const std::string & partPath)236 bool File::CreatePartDir(const std::string &partPath)
237 {
238 struct stat st;
239 if (stat(partPath.c_str(), &st) < 0) {
240 if (errno != ENOENT) {
241 return false;
242 }
243
244 #ifndef __MINGW32__
245 if (mkdir(partPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
246 #else
247 if (mkdir(partPath.c_str()) < 0) {
248 #endif
249 return false;
250 }
251 } else if (!S_ISDIR(st.st_mode)) {
252 return false;
253 }
254 return true;
255 }
256
257 std::string File::AdapterPath(const std::string &path)
258 {
259 #ifndef __MINGW32__
260 std::string newPath = StringHelper::Replace(path, '\\', '/');
261 #else
262 std::string newPath = StringHelper::Replace(path, '/', '\\');
263 #endif
264
265 // "foo/v1_0//ifoo.h" -> "foo/v1_0/ifoo.h"
266 StringBuilder adapterPath;
267 bool hasSep = false;
268 for (size_t i = 0; i < newPath.size(); i++) {
269 char c = newPath[i];
270 if (c == SEPARATOR) {
271 if (hasSep) {
272 continue;
273 }
274 adapterPath.Append(c);
275 hasSep = true;
276 } else {
277 adapterPath.Append(c);
278 hasSep = false;
279 }
280 }
281 return adapterPath.ToString();
282 }
283
284 std::string File::AdapterRealPath(const std::string &path)
285 {
286 if (path.empty()) {
287 return "";
288 }
289 return RealPath(File::AdapterPath(path));
290 }
291
292 std::string File::RealPath(const std::string &path)
293 {
294 if (path.empty()) {
295 return "";
296 }
297
298 char realPath[PATH_MAX + 1];
299 #ifdef __MINGW32__
300 char *absPath = _fullpath(realPath, path.c_str(), PATH_MAX);
301 #else
302 char *absPath = realpath(path.c_str(), realPath);
303 #endif
304 return absPath == nullptr ? "" : absPath;
305 }
306
307 std::string File::CanonicalPath(const std::string &path)
308 {
309 std::istringstream ss(path);
310 std::string segment;
311 std::vector<std::string> pathStack;
312
313 while (std::getline(ss, segment, SEPARATOR)) {
314 if (segment == "..") {
315 if (!pathStack.empty()) {
316 pathStack.pop_back();
317 }
318 } else if (!segment.empty() && segment != ".") {
319 pathStack.push_back(segment);
320 }
321 }
322
323 std::string normalizedPath;
324 for (const auto &s : pathStack) {
325 normalizedPath += std::string(1, SEPARATOR) + s;
326 }
327 #ifdef __MINGW32__
328 normalizedPath = normalizedPath.substr(1);
329 #endif
330
331 return normalizedPath.empty() ? std::string(1, SEPARATOR) : normalizedPath;
332 }
333
334 std::string File::AbsolutePath(const std::string &path)
335 {
336 if (!path.empty() && (path[0] == SEPARATOR || path[1] == ':')) {
337 return path;
338 }
339 char buffer[PATH_MAX + 1];
340 #ifdef __MINGW32__
341 if (_getcwd(buffer, sizeof(buffer)) != nullptr) {
342 #else
343 if (getcwd(buffer, sizeof(buffer)) != nullptr) {
344 #endif
345 return std::string(buffer) + std::string(1, SEPARATOR) + path;
346 }
347 return "";
348 }
349
350 std::vector<std::string> File::SplitPath(const std::string& path)
351 {
352 std::vector<std::string> components;
353 std::istringstream stream(path);
354 std::string segment;
355
356 while (std::getline(stream, segment, SEPARATOR)) {
357 if (!segment.empty()) {
358 components.push_back(segment);
359 }
360 }
361
362 return components;
363 }
364
365 std::string File::RelativePath(const std::string& pathA, const std::string& pathB)
366 {
367 std::vector<std::string> componentsA = SplitPath(pathA);
368 std::vector<std::string> componentsB = SplitPath(pathB);
369
370 size_t i = 0;
371 while (i < componentsA.size() && i < componentsB.size() && componentsA[i] == componentsB[i]) {
372 i++;
373 }
374
375 std::string relativePath;
376
377 for (size_t j = i; j < componentsB.size(); ++j) {
378 relativePath += ".." + std::string(1, SEPARATOR);
379 }
380
381 for (size_t j = i; j < componentsA.size(); ++j) {
382 relativePath += componentsA[j] + std::string(1, SEPARATOR);
383 }
384
385 if (!relativePath.empty() && relativePath.back() == SEPARATOR) {
386 relativePath.pop_back();
387 }
388
389 return relativePath;
390 }
391
392 bool File::CheckValid(const std::string &path)
393 {
394 if (access(path.c_str(), F_OK | R_OK | W_OK) != 0) {
395 return false;
396 }
397
398 struct stat st;
399 if (stat(path.c_str(), &st) < 0) {
400 return false;
401 }
402
403 if (S_ISDIR(st.st_mode)) {
404 return false;
405 }
406
407 return true;
408 }
409
410 std::set<std::string> File::FindFiles(const std::string &rootDir)
411 {
412 if (rootDir.empty()) {
413 return std::set<std::string>();
414 }
415
416 std::set<std::string> files;
417 std::queue<std::string> dirs;
418 dirs.push(rootDir);
419 while (!dirs.empty()) {
420 std::string dirPath = dirs.front().back() == SEPARATOR ? dirs.front() : dirs.front() + SEPARATOR;
421 dirs.pop();
422 DIR *dir = opendir(dirPath.c_str());
423 if (dir == nullptr) {
424 Logger::E(TAG, "failed to open '%s', errno:%d", dirPath.c_str(), errno);
425 continue;
426 }
427
428 struct dirent *dirInfo = readdir(dir);
429 for (; dirInfo != nullptr; dirInfo = readdir(dir)) {
430 if (strcmp(dirInfo->d_name, ".") == 0 || strcmp(dirInfo->d_name, "..") == 0) {
431 continue;
432 }
433 #ifndef __MINGW32__
434 if (dirInfo->d_type == DT_REG && StringHelper::EndWith(dirInfo->d_name, ".idl")) {
435 std::string filePath = dirPath + dirInfo->d_name;
436 files.insert(filePath);
437 continue;
438 }
439
440 if (dirInfo->d_type == DT_DIR) {
441 dirs.emplace(dirPath + dirInfo->d_name);
442 continue;
443 }
444 #else
445 std::string filePath = dirPath + dirInfo->d_name;
446 struct stat fileInfo;
447 if ((stat(filePath.c_str(), &fileInfo) == 0) && S_ISREG(fileInfo.st_mode)) {
448 files.insert(filePath);
449 }
450 #endif
451 }
452 closedir(dir);
453 }
454
455 return files;
456 }
457
458 size_t File::GetHashKey()
459 {
460 StringBuilder fileStr;
461 while (!IsEof()) {
462 fileStr.Append(GetChar());
463 }
464
465 return std::hash<std::string>()(fileStr.ToString());
466 }
467 } // namespace Idl
468 } // namespace OHOS