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 <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 
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     fd_ = fopen(realPath.c_str(), "r");
81     if (fd_ == nullptr) {
82         Logger::E(TAG, "can't open '%s'", realPath.c_str());
83         return;
84     }
85 
86     path_ = realPath;
87     PeekChar();
88 }
89 
GetChar()90 char File::GetChar()
91 {
92     char c = PeekChar();
93 
94     if (position_ + 1 <= size_) {
95         position_++;
96 
97         if (c != '\n') {
98             columnNo_++;
99         } else {
100             columnNo_ = 1;
101             lineNo_++;
102         }
103     }
104     return c;
105 }
106 
PeekChar()107 char File::PeekChar()
108 {
109     if (position_ + 1 > size_) {
110         size_t size = Read();
111         if (size == 0) {
112             isEof_ = true;
113         }
114     }
115 
116     return buffer_[position_];
117 }
118 
IsEof() const119 bool File::IsEof() const
120 {
121     return isEof_ || buffer_[position_] == static_cast<char>(-1);
122 }
123 
Read()124 size_t File::Read()
125 {
126     if (isEof_ || isError_) {
127         return -1;
128     }
129 
130     std::fill(buffer_, buffer_ + BUFFER_SIZE, 0);
131     size_t count = fread(buffer_, 1, BUFFER_SIZE - 1, fd_);
132     if (count < BUFFER_SIZE - 1) {
133         isError_ = ferror(fd_) != 0;
134         buffer_[count] = -1;
135     }
136     size_ = count;
137     position_ = 0;
138     if ((count == 0) || (count >= BUFFER_SIZE)) {
139         return -1;
140     }
141     return count;
142 }
143 
ReadData(void * data,size_t size) const144 size_t File::ReadData(void *data, size_t size) const
145 {
146     if (data == nullptr || size == 0) {
147         return 0;
148     }
149 
150     if (fd_ == nullptr) {
151         return 0;
152     }
153 
154     return fread(data, 1, size, fd_);
155 }
156 
WriteData(const void * data,size_t size) const157 bool File::WriteData(const void *data, size_t size) const
158 {
159     if (data == nullptr || size == 0) {
160         return true;
161     }
162 
163     if (fd_ == nullptr || !(mode_ & (WRITE | APPEND))) {
164         return false;
165     }
166 
167     size_t count = fwrite(data, size, 1, fd_);
168     return count == 1;
169 }
170 
Flush() const171 void File::Flush() const
172 {
173     if ((mode_ & (WRITE | APPEND)) && fd_ != nullptr) {
174         fflush(fd_);
175     }
176 }
177 
Reset() const178 bool File::Reset() const
179 {
180     if (fd_ == nullptr) {
181         return false;
182     }
183 
184     return fseek(fd_, 0, SEEK_SET) == 0;
185 }
186 
Skip(long size) const187 bool File::Skip(long size) const
188 {
189     if (fd_ == nullptr) {
190         return false;
191     }
192 
193     return fseek(fd_, size, SEEK_CUR) == 0;
194 }
195 
Close()196 void File::Close()
197 {
198     if (fd_ != nullptr) {
199         fclose(fd_);
200         fd_ = nullptr;
201     }
202 }
203 
CreateParentDir(const std::string & path)204 bool File::CreateParentDir(const std::string &path)
205 {
206     if (access(path.c_str(), F_OK | R_OK | W_OK) == 0) {
207         return true;
208     }
209 
210     size_t pos = 1;
211     while ((pos = path.find(SEPARATOR, pos)) != std::string::npos) {
212         std::string partPath = StringHelper::SubStr(path, 0, pos);
213         partPath += SEPARATOR;
214         if (File::CreatePartDir(partPath) == false) {
215             return false;
216         }
217         pos += 1;
218     }
219     return true;
220 }
221 
CreatePartDir(const std::string & partPath)222 bool File::CreatePartDir(const std::string &partPath)
223 {
224     struct stat st;
225     if (stat(partPath.c_str(), &st) < 0) {
226         if (errno != ENOENT) {
227             return false;
228         }
229 
230 #ifndef __MINGW32__
231         if (mkdir(partPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
232 #else
233         if (mkdir(partPath.c_str()) < 0) {
234 #endif
235             return false;
236         }
237     } else if (!S_ISDIR(st.st_mode)) {
238         return false;
239     }
240     return true;
241 }
242 
243 std::string File::AdapterPath(const std::string &path)
244 {
245 #ifndef __MINGW32__
246     std::string newPath = StringHelper::Replace(path, '\\', '/');
247 #else
248     std::string newPath = StringHelper::Replace(path, '/', '\\');
249 #endif
250 
251     // "foo/v1_0//ifoo.h" -> "foo/v1_0/ifoo.h"
252     StringBuilder adapterPath;
253     bool hasSep = false;
254     for (size_t i = 0; i < newPath.size(); i++) {
255         char c = newPath[i];
256         if (c == SEPARATOR) {
257             if (hasSep) {
258                 continue;
259             }
260             adapterPath.Append(c);
261             hasSep = true;
262         } else {
263             adapterPath.Append(c);
264             hasSep = false;
265         }
266     }
267     return adapterPath.ToString();
268 }
269 
270 std::string File::AdapterRealPath(const std::string &path)
271 {
272     if (path.empty()) {
273         return "";
274     }
275     return RealPath(File::AdapterPath(path));
276 }
277 
278 std::string File::RealPath(const std::string &path)
279 {
280     if (path.empty()) {
281         return "";
282     }
283 
284     char realPath[PATH_MAX + 1];
285 #ifdef __MINGW32__
286     char *absPath = _fullpath(realPath, path.c_str(), PATH_MAX);
287 #else
288     char *absPath = realpath(path.c_str(), realPath);
289 #endif
290     return absPath == nullptr ? "" : absPath;
291 }
292 
293 bool File::CheckValid(const std::string &path)
294 {
295     if (access(path.c_str(), F_OK | R_OK | W_OK) != 0) {
296         return false;
297     }
298 
299     struct stat st;
300     if (stat(path.c_str(), &st) < 0) {
301         return false;
302     }
303 
304     if (S_ISDIR(st.st_mode)) {
305         return false;
306     }
307 
308     return true;
309 }
310 
311 std::set<std::string> File::FindFiles(const std::string &rootDir)
312 {
313     if (rootDir.empty()) {
314         return std::set<std::string>();
315     }
316 
317     std::set<std::string> files;
318     std::queue<std::string> dirs;
319     dirs.push(rootDir);
320     while (!dirs.empty()) {
321         std::string dirPath = dirs.front().back() == SEPARATOR ? dirs.front() : dirs.front() + SEPARATOR;
322         dirs.pop();
323         DIR *dir = opendir(dirPath.c_str());
324         if (dir == nullptr) {
325             Logger::E(TAG, "failed to open '%s', errno:%d", dirPath.c_str(), errno);
326             continue;
327         }
328 
329         struct dirent *dirInfo = readdir(dir);
330         for (; dirInfo != nullptr; dirInfo = readdir(dir)) {
331             if (strcmp(dirInfo->d_name, ".") == 0 || strcmp(dirInfo->d_name, "..") == 0) {
332                 continue;
333             }
334 #ifndef __MINGW32__
335             if (dirInfo->d_type == DT_REG && StringHelper::EndWith(dirInfo->d_name, ".idl")) {
336                 std::string filePath = dirPath + dirInfo->d_name;
337                 files.insert(filePath);
338                 continue;
339             }
340 
341             if (dirInfo->d_type == DT_DIR) {
342                 dirs.emplace(dirPath + dirInfo->d_name);
343                 continue;
344             }
345 #else
346             std::string filePath = dirPath + dirInfo->d_name;
347             struct stat fileInfo;
348             if ((stat(filePath.c_str(), &fileInfo) == 0) && S_ISREG(fileInfo.st_mode)) {
349                 files.insert(filePath);
350             }
351 #endif
352         }
353         closedir(dir);
354     }
355 
356     return files;
357 }
358 
359 size_t File::GetHashKey()
360 {
361     StringBuilder fileStr;
362     while (!IsEof()) {
363         fileStr.Append(GetChar());
364     }
365 
366     return std::hash<std::string>()(fileStr.ToString());
367 }
368 } // namespace Idl
369 } // namespace OHOS