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
35namespace OHOS {
36namespace Idl {
37File::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
62File::~File()
63{
64    Close();
65}
66
67void 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
90char 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
107char 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
119bool File::IsEof() const
120{
121    return isEof_ || buffer_[position_] == static_cast<char>(-1);
122}
123
124size_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
144size_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
157bool 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
171void File::Flush() const
172{
173    if ((mode_ & (WRITE | APPEND)) && fd_ != nullptr) {
174        fflush(fd_);
175    }
176}
177
178bool File::Reset() const
179{
180    if (fd_ == nullptr) {
181        return false;
182    }
183
184    return fseek(fd_, 0, SEEK_SET) == 0;
185}
186
187bool 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
196void File::Close()
197{
198    if (fd_ != nullptr) {
199        fclose(fd_);
200        fd_ = nullptr;
201    }
202}
203
204bool 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
222bool 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
243std::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
270std::string File::AdapterRealPath(const std::string &path)
271{
272    if (path.empty()) {
273        return "";
274    }
275    return RealPath(File::AdapterPath(path));
276}
277
278std::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
293bool 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
311std::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
359size_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