1/**
2 * Copyright (c) 2022 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 "assembler/assembly-function.h"
17#include "libpandafile/literal_data_accessor.h"
18#include "os/file.h"
19#include "options.h"
20
21#include "mergeProgram.h"
22
23#if defined(PANDA_TARGET_WINDOWS)
24#include <io.h>
25#else
26#include <dirent.h>
27#endif
28
29#include <fstream>
30
31namespace panda::proto {
32#if PANDA_TARGET_WINDOWS
33bool MergeProgram::EnumerateFilesWindows(const std::string &protoBinPath, const std::string &protoBinSuffix,
34                                         std::vector<std::string> &directoryFiles)
35{
36    int handle = 0;
37    struct _finddata_t fileInfo;
38    std::string path;
39    if ((handle = _findfirst(path.assign(protoBinPath).append("\\*").c_str(), &fileInfo)) == -1) {
40        return false;
41    }
42    do {
43        if (fileInfo.attrib & _A_SUBDIR) {
44            if ((!strncmp(fileInfo.name, ".", 1)) || (!strncmp(fileInfo.name, "..", 2))) {
45                continue;
46            }
47            if (!GetProtoFiles(path.assign(protoBinPath).append("\\").append(fileInfo.name), protoBinSuffix,
48                               directoryFiles)) {
49                _findclose(handle);
50                return false;
51            }
52        } else {
53            std::string fileName(fileInfo.name);
54            if (fileName.substr(fileName.find_last_of(".") + 1).compare(protoBinSuffix) == 0) {
55                directoryFiles.emplace_back(path.assign(protoBinPath).append("\\").append(fileName));
56            }
57        }
58    } while (_findnext(handle, &fileInfo) == 0);
59    _findclose(handle);
60    return true;
61}
62
63#elif PANDA_TARGET_UNIX
64bool MergeProgram::EnumerateFilesUnix(const std::string &protoBinPath, const std::string &protoBinSuffix,
65                                      std::vector<std::string> &directoryFiles)
66{
67    DIR *protoBin = opendir(protoBinPath.c_str());
68    if (protoBin == nullptr) {
69        return false;
70    }
71    dirent *dir = nullptr;
72    std::string pathPrefix = protoBinPath + "/";
73    while ((dir = readdir(protoBin)) != nullptr) {
74        if ((!strncmp(dir->d_name, ".", 1)) || (!strncmp(dir->d_name, "..", 2))) {
75            continue;
76        }
77        if (dir->d_type == DT_DIR) {
78            std::string subDirName = pathPrefix + dir->d_name;
79            if (!GetProtoFiles(subDirName, protoBinSuffix, directoryFiles)) {
80                closedir(protoBin);
81                return false;
82            }
83        } else {
84            std::string fileName = pathPrefix + dir->d_name;
85            if (fileName.substr(fileName.find_last_of(".") + 1).compare(protoBinSuffix) == 0) {
86                directoryFiles.emplace_back(fileName);
87            }
88        }
89    }
90    closedir(protoBin);
91    return true;
92}
93#endif
94
95bool MergeProgram::GetProtoFiles(const std::string &protoBinPath, const std::string &protoBinSuffix,
96                                 std::vector<std::string> &directoryFiles)
97{
98#if PANDA_TARGET_WINDOWS
99    return EnumerateFilesWindows(protoBinPath, protoBinSuffix, directoryFiles);
100#elif PANDA_TARGET_UNIX
101    return EnumerateFilesUnix(protoBinPath, protoBinSuffix, directoryFiles);
102#endif
103}
104
105bool MergeProgram::AppendProtoFiles(const std::string &filePath, const std::string &protoBinSuffix,
106                                    std::vector<std::string> &protoFiles)
107{
108    auto inputAbs = panda::os::file::File::GetAbsolutePath(filePath);
109    if (!inputAbs) {
110        std::cerr << "Failed to open: " << filePath << std::endl;
111        return false;
112    }
113
114    auto fPath = inputAbs.Value();
115    if (panda::os::file::File::IsRegularFile(fPath)) {
116        if (filePath.substr(filePath.find_last_of(".") + 1).compare(protoBinSuffix) == 0) {
117            protoFiles.emplace_back(fPath);
118        }
119    } else if (panda::os::file::File::IsDirectory(fPath)) {
120        std::vector<std::string> directoryFiles;
121        if (!GetProtoFiles(fPath, protoBinSuffix, directoryFiles)) {
122            return false;
123        }
124        protoFiles.insert(protoFiles.end(), directoryFiles.begin(), directoryFiles.end());
125    } else {
126        std::cerr << "The input path: " << fPath << " must be either a regular file or directory." << std::endl;
127        return false;
128    }
129
130    return true;
131}
132
133bool MergeProgram::CollectProtoFiles(std::string &input, const std::string &protoBinSuffix,
134                                     std::vector<std::string> &protoFiles)
135{
136    constexpr const char DOGGY = '@';
137    std::vector<std::string> inputs;
138    bool isList = false;
139
140    if (input[0] == DOGGY) {
141        input.erase(input.begin() + 0);
142        isList = true;
143    }
144
145    auto inputAbs = panda::os::file::File::GetAbsolutePath(input);
146    if (!inputAbs) {
147        std::cerr << "Failed to open: " << input << std::endl;
148        return false;
149    }
150    if (isList) {
151        std::ifstream in(panda::os::file::File::GetExtendedFilePath(inputAbs.Value()));
152        std::string line;
153        constexpr const char CARRIAGE = '\r';
154        while (getline(in, line)) {
155            // erase front spaces
156            line.erase(line.begin(),
157                       std::find_if(line.begin(), line.end(), [](unsigned char ch) { return std::isspace(ch) == 0; }));
158            // erase carrige return symbol (Windows workaround)
159            line.erase(std::find_if(line.rbegin(), line.rend(), [](unsigned char ch) { return ch != CARRIAGE; }).base(),
160                       line.end());
161            if (!line.empty()) {
162                inputs.push_back(line);
163            }
164        }
165        in.close();
166    } else {
167        inputs.push_back(inputAbs.Value());
168    }
169
170    protoFiles.reserve(inputs.size());
171    for (auto &filePath : inputs) {
172        if (!AppendProtoFiles(filePath, protoBinSuffix, protoFiles)) {
173            return false;
174        }
175    }
176
177    return true;
178}
179} // namespace panda::proto
180