• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/io_util.h"
17 
18 #include <charconv>
19 
20 #include <base/math/mathf.h>
21 
22 #include <core/io/intf_file_manager.h>
23 #include <core/json/json.h>
24 #include <core/intf_engine.h>
25 
26 #include "util/path_util.h"
27 #include "util/json_util.h"
28 
29 using namespace BASE_NS;
30 using namespace CORE_NS;
31 
32 UTIL_BEGIN_NAMESPACE()
33 
34 namespace IoUtil {
35 
WriteCompatibilityInfo(json::standalone_value & jsonOut,const CompatibilityInfo & info)36 bool WriteCompatibilityInfo(json::standalone_value& jsonOut, const CompatibilityInfo& info)
37 {
38     jsonOut["compatibility_info"] = json::standalone_value::object();
39     jsonOut["compatibility_info"]["version"] =
40         string(to_string(info.versionMajor) + "." + to_string(info.versionMinor));
41     jsonOut["compatibility_info"]["type"] = string(info.type);
42     return true;
43 }
44 
CheckCompatibility(const json::value & json,array_view<CompatibilityRange const> validVersions)45 SerializationResult CheckCompatibility(const json::value& json, array_view<CompatibilityRange const> validVersions)
46 {
47     SerializationResult result;
48 
49     string type;
50     string version;
51     if (const json::value* iter = json.find("compatibility_info"); iter) {
52         string parseError;
53         SafeGetJsonValue(*iter, "type", parseError, type);
54         SafeGetJsonValue(*iter, "version", parseError, version);
55 
56         uint32_t versionMajor{ 0 };
57         uint32_t versionMinor{ 0 };
58         if (const auto delim = version.find('.'); delim != string::npos) {
59             std::from_chars(version.data(), version.data() + delim, versionMajor);
60             const size_t minorStart = delim + 1;
61             std::from_chars(version.data() + minorStart, version.data() + version.size(), versionMinor);
62         } else {
63             std::from_chars(version.data(), version.data() + version.size(), versionMajor);
64         }
65 
66         result.compatibilityInfo.versionMajor = versionMajor;
67         result.compatibilityInfo.versionMinor = versionMinor;
68         result.compatibilityInfo.type = type;
69 
70         for (const auto& range : validVersions) {
71             if (type != range.type) {
72                 continue;
73             }
74             if ((range.versionMajorMin != CompatibilityRange::IGNORE_VERSION) &&
75                 (versionMajor < range.versionMajorMin)) {
76                 continue;
77             }
78             if ((range.versionMajorMax != CompatibilityRange::IGNORE_VERSION) &&
79                 (versionMajor > range.versionMajorMax)) {
80                 continue;
81             }
82             if ((range.versionMinorMin != CompatibilityRange::IGNORE_VERSION) &&
83                 (versionMinor < range.versionMinorMin)) {
84                 continue;
85             }
86             if ((range.versionMinorMax != CompatibilityRange::IGNORE_VERSION) &&
87                 (versionMinor > range.versionMinorMax)) {
88                 continue;
89             }
90 
91             // A compatible version was found from the list of valid versions.
92             return result;
93         }
94     }
95 
96     // Not a compatible version.
97     result.status = Status::ERROR_COMPATIBILITY_MISMATCH;
98     result.error = "Unsupported version. type: '" + type + "' version: '" + version + "'";
99     return result;
100 }
101 
CreateDirectories(CORE_NS::IFileManager & fileManager,string_view pathUri)102 bool CreateDirectories(CORE_NS::IFileManager& fileManager, string_view pathUri)
103 {
104     // Verify that the target path exists. (and create missing ones)
105     // Remove protocol.
106     auto pos = pathUri.find("://") + 3;
107     for (;;) {
108         size_t end = pathUri.find('/', pos);
109         auto part = pathUri.substr(0, end);
110 
111         // The last "part" should be a file name, so terminate there.
112         if (end == string_view::npos) {
113             break;
114         }
115 
116         auto entry = fileManager.GetEntry(part);
117         if (entry.type == entry.UNKNOWN) {
118             fileManager.CreateDirectory(part);
119         } else if (entry.type == entry.DIRECTORY) {
120         } else if (entry.type == entry.FILE) {
121             // Invalid path..
122             return false;
123         }
124         pos = end + 1;
125     }
126     return true;
127 }
128 
DeleteDirectory(CORE_NS::IFileManager & fileManager,string_view pathUri)129 bool DeleteDirectory(CORE_NS::IFileManager& fileManager, string_view pathUri)
130 {
131     auto dir = fileManager.OpenDirectory(pathUri);
132     if (!dir) {
133         return false;
134     }
135 
136     bool result = true;
137 
138     for (auto& entry : dir->GetEntries()) {
139         auto childUri = PathUtil::ResolvePath(pathUri, entry.name);
140         switch (entry.type) {
141             case IDirectory::Entry::Type::FILE:
142                 result = fileManager.DeleteFile(childUri) && result;
143                 break;
144             case IDirectory::Entry::Type::DIRECTORY:
145                 result = DeleteDirectory(fileManager, childUri) && result;
146                 break;
147             default:
148                 // NOTE: currently unknown type is just ignored and does not affect the result.
149                 break;
150         }
151     }
152 
153     // Result is true if all copy operations succeeded.
154     return fileManager.DeleteDirectory(pathUri) && result;
155 }
156 
Copy(CORE_NS::IFileManager & fileManager,string_view sourceUri,string_view destinationUri)157 bool Copy(CORE_NS::IFileManager& fileManager, string_view sourceUri, string_view destinationUri)
158 {
159     bool destinationIsDir = false;
160     string tmp; // Just to keep the string alive for this function scope.
161 
162     // If the destination is a directory. copy the source into that dir.
163     {
164         auto destinationDir = fileManager.OpenDirectory(destinationUri);
165         if (destinationDir) {
166             destinationIsDir = true;
167             auto filename = PathUtil::GetFilename(sourceUri);
168             tmp = PathUtil::ResolvePath(destinationUri, filename);
169             destinationUri = tmp;
170         }
171     }
172 
173     // First try copying as a file.
174     if (CopyFile(fileManager, sourceUri, destinationUri)) {
175         return true;
176     }
177 
178     // Then try copying as a dir (if the destination is a dir).
179     if (destinationIsDir) {
180         auto destinationUriAsDir = destinationUri + "/";
181         CreateDirectories(fileManager, destinationUriAsDir);
182         return CopyDirectoryContents(fileManager, sourceUri, destinationUriAsDir);
183     }
184 
185     return false;
186 }
187 
Move(CORE_NS::IFileManager & fileManager,string_view sourceUri,string_view destinationUri)188 bool Move(CORE_NS::IFileManager& fileManager, string_view sourceUri, string_view destinationUri)
189 {
190     return fileManager.Rename(sourceUri, destinationUri);
191 }
192 
CopyFile(CORE_NS::IFileManager & fileManager,string_view sourceUri,string_view destinationUri)193 bool CopyFile(CORE_NS::IFileManager& fileManager, string_view sourceUri, string_view destinationUri)
194 {
195     auto s = fileManager.OpenFile(sourceUri);
196     if (!s) {
197         return false;
198     }
199     if (!CreateDirectories(fileManager, destinationUri)) {
200         return false;
201     }
202     auto d = fileManager.CreateFile(destinationUri);
203     if (!d) {
204         return false;
205     }
206     constexpr size_t bufferSize = 65536; // copy in 64kb blocks
207     uint8_t buffer[bufferSize];
208     size_t total = s->GetLength();
209     while (total > 0) {
210         auto todo = Math::min(total, bufferSize);
211         if (todo != s->Read(buffer, todo)) {
212             return false;
213         }
214         if (todo != d->Write(buffer, todo)) {
215             return false;
216         }
217         total -= todo;
218     }
219     return true;
220 }
221 
CopyDirectoryContents(CORE_NS::IFileManager & fileManager,string_view sourceUri,string_view destinationUri)222 bool CopyDirectoryContents(CORE_NS::IFileManager& fileManager, string_view sourceUri, string_view destinationUri)
223 {
224     auto dst = fileManager.OpenDirectory(destinationUri);
225     if (!dst) {
226         return false;
227     }
228 
229     auto src = fileManager.OpenDirectory(sourceUri);
230     if (!src) {
231         return false;
232     }
233 
234     bool result = true;
235 
236     for (auto& entry : src->GetEntries()) {
237         auto childSrc = PathUtil::ResolvePath(sourceUri, entry.name);
238         auto childDst = PathUtil::ResolvePath(destinationUri, entry.name);
239 
240         switch (entry.type) {
241             case IDirectory::Entry::Type::FILE:
242                 result = CopyFile(fileManager, childSrc, childDst) && result;
243                 break;
244             case IDirectory::Entry::Type::DIRECTORY:
245                 if (auto dirExists = fileManager.OpenDirectory(childDst); !dirExists) {
246                     if (!fileManager.CreateDirectory(childDst)) {
247                         result = false;
248                         continue;
249                     }
250                 }
251                 result = CopyDirectoryContents(fileManager, childSrc, childDst) && result;
252                 break;
253             default:
254                 // NOTE: currently unknown type is just ignored and does not affect the result.
255                 break;
256         }
257     }
258 
259     // Result is true if all copy operations succeeded.
260     return result;
261 }
262 
SaveTextFile(CORE_NS::IFileManager & fileManager,string_view fileUri,string_view fileContents)263 bool SaveTextFile(CORE_NS::IFileManager& fileManager, string_view fileUri, string_view fileContents)
264 {
265     // the safest way to save files would be to save to a temp file and rename.
266     auto file = fileManager.CreateFile(fileUri);
267     if (file) {
268         file->Write(fileContents.data(), fileContents.length());
269         file->Close();
270         return true;
271     }
272 
273     return false;
274 }
275 
LoadTextFile(CORE_NS::IFileManager & fileManager,string_view fileUri,string & fileContentsOut)276 bool LoadTextFile(CORE_NS::IFileManager& fileManager, string_view fileUri, string& fileContentsOut)
277 {
278     auto file = fileManager.OpenFile(fileUri);
279     if (file) {
280         const size_t length = file->GetLength();
281         fileContentsOut.resize(length);
282         return file->Read(fileContentsOut.data(), length) == length;
283     }
284     return false;
285 }
286 
287 template<typename Work>
ReplaceTextInFilesImpl(CORE_NS::IFileManager & fileManager,BASE_NS::string_view folderUri,Work & replace)288 void ReplaceTextInFilesImpl(CORE_NS::IFileManager& fileManager, BASE_NS::string_view folderUri, Work& replace)
289 {
290     if (auto dir = fileManager.OpenDirectory(folderUri)) {
291         auto entries = dir->GetEntries();
292         for (const auto& entry : entries) {
293             if (entry.type == CORE_NS::IDirectory::Entry::FILE) {
294                 auto separator_pos = entry.name.find_last_of(".");
295                 auto ending = entry.name.substr(separator_pos);
296                 bool isPlaintext { false };
297                 static BASE_NS::vector<BASE_NS::string_view> plaintextTypes {
298                     ".txt",   // C++ Buld files
299                     ".cpp",   // C++ source files
300                     ".h",     // C++ header files
301                     ".json",  // Configuration files
302                     ".json5", // Harmony OS build scripts
303                     ".cmake", // Build files
304                     ".in",    // CMake configuration files
305                     ".ets"    // Harmony OS app code
306                 };
307                 for (const auto& type : plaintextTypes) {
308                     if (ending == type) {
309                         isPlaintext = true;
310                     }
311                 }
312                 // odds of getting a match in a binary just by chance seem pretty slim so perhaps this check
313                 // could be omitted, but I suppose that depends on the length of the tag we're replacing
314                 if (isPlaintext) {
315                     auto inFilePath = PathUtil::ResolvePath(folderUri, entry.name);
316                     ReplaceTextInFileImpl(fileManager, inFilePath, replace);
317                 }
318             } else if (entry.type == CORE_NS::IDirectory::Entry::DIRECTORY) {
319                 auto path = PathUtil::ResolvePath(folderUri, entry.name);
320                 ReplaceTextInFilesImpl(fileManager, path, replace);
321             }
322         }
323     }
324 }
325 
326 template<typename Work>
ReplaceTextInFileImpl(CORE_NS::IFileManager & fileManager,BASE_NS::string_view uri,Work & replace)327 void ReplaceTextInFileImpl(CORE_NS::IFileManager& fileManager, BASE_NS::string_view uri, Work& replace)
328 {
329     auto inFile = fileManager.OpenFile(uri);
330     if (inFile) {
331         BASE_NS::string stringContents;
332         size_t len = inFile->GetLength();
333         stringContents.resize(len);
334         inFile->Read(stringContents.data(), len);
335 
336         replace(stringContents);
337 
338         auto dataToWrite = stringContents.data();
339         auto lenToWrite = stringContents.length();
340         fileManager.DeleteFile(uri);
341         auto outFile = fileManager.CreateFile(uri);
342         if (outFile) {
343             outFile->Write(dataToWrite, lenToWrite);
344         }
345     }
346 }
347 
ReplaceTextInFiles(CORE_NS::IFileManager & fileManager,BASE_NS::string_view folderUri,BASE_NS::string_view text,BASE_NS::string_view replaceWith)348 void ReplaceTextInFiles(CORE_NS::IFileManager& fileManager, BASE_NS::string_view folderUri, BASE_NS::string_view text,
349     BASE_NS::string_view replaceWith)
350 {
351     auto replace = [&text, &replaceWith](BASE_NS::string& stringContents) {
352         auto pos = stringContents.find(text, 0UL);
353         while (pos != BASE_NS::string::npos) {
354             stringContents = stringContents.replace(
355                 stringContents.begin() + static_cast<int64_t>(pos), stringContents.begin() +
356                     static_cast<int64_t>(pos + text.size()), replaceWith);
357             pos += replaceWith.size();
358             pos = stringContents.find(text, pos);
359         }
360     };
361     ReplaceTextInFilesImpl(fileManager, folderUri, replace);
362 }
363 
ReplaceTextInFiles(CORE_NS::IFileManager & fileManager,BASE_NS::string_view folderUri,BASE_NS::vector<Replacement> replacements)364 void ReplaceTextInFiles(
365     CORE_NS::IFileManager& fileManager, BASE_NS::string_view folderUri, BASE_NS::vector<Replacement> replacements)
366 {
367     auto replace = [&replacements](BASE_NS::string& stringContents) {
368         for (const auto& repl : replacements) {
369             auto pos = stringContents.find(repl.from, 0UL);
370             while (pos != BASE_NS::string::npos) {
371                 stringContents = stringContents.replace(
372                     stringContents.begin() + static_cast<int64_t>(pos), stringContents.begin() +
373 		        static_cast<int64_t>(pos + repl.from.size()), repl.to);
374                 pos += repl.to.size();
375                 pos = stringContents.find(repl.from, pos);
376             }
377         }
378     };
379     ReplaceTextInFilesImpl(fileManager, folderUri, replace);
380 }
381 
ReplaceTextInFile(CORE_NS::IFileManager & fileManager,BASE_NS::string_view uri,BASE_NS::vector<Replacement> replacements)382 void ReplaceTextInFile(
383     CORE_NS::IFileManager& fileManager, BASE_NS::string_view uri, BASE_NS::vector<Replacement> replacements)
384 {
385     auto replace = [&replacements](BASE_NS::string& stringContents) {
386         for (const auto& repl : replacements) {
387             auto pos = stringContents.find(repl.from, 0UL);
388             while (pos != BASE_NS::string::npos) {
389                 stringContents = stringContents.replace(stringContents.begin() + static_cast<int64_t>(pos),
390                     stringContents.begin() + static_cast<int64_t>(pos + repl.from.size()), repl.to);
391                 pos += repl.to.size();
392                 pos = stringContents.find(repl.from, pos);
393             }
394         }
395     };
396     ReplaceTextInFileImpl(fileManager, uri, replace);
397 }
398 
CopyAndRenameFiles(CORE_NS::IFileManager & fileManager,BASE_NS::string_view fromUri,BASE_NS::string_view toUri,BASE_NS::string_view filename)399 bool CopyAndRenameFiles(CORE_NS::IFileManager& fileManager, BASE_NS::string_view fromUri, BASE_NS::string_view toUri,
400     BASE_NS::string_view filename)
401 {
402     auto template_dir = fileManager.OpenDirectory(fromUri);
403     auto project_dir = fileManager.OpenDirectory(toUri);
404     bool result{ true };
405     // copy the .cpp and .h behavior files from the template to the project, renaming them
406     if (template_dir && project_dir) {
407         for (const auto& entry : template_dir->GetEntries()) {
408             if (entry.type != CORE_NS::IDirectory::Entry::Type::FILE) {
409                 continue;
410             }
411             const auto& n = entry.name;
412             auto ending = n.substr(n.find_last_of('.'));
413             auto from = PathUtil::ResolvePath(fromUri, n);
414             auto to = PathUtil ::ResolvePath(toUri, filename + ending);
415             if (!CopyFile(fileManager, from, to)) {
416                 result = false;
417                 break;
418             }
419         }
420     } else {
421         result = false;
422     }
423     return result;
424 }
425 
426 template<typename Work>
InsertInFileBoilerplate(CORE_NS::IFileManager & fileManager,BASE_NS::string_view fileUri,Work & inner)427 void InsertInFileBoilerplate(CORE_NS::IFileManager& fileManager, BASE_NS::string_view fileUri, Work& inner)
428 {
429     if (auto inFile = fileManager.OpenFile(fileUri)) {
430         BASE_NS::string stringContents;
431         size_t len = inFile->GetLength();
432         stringContents.resize(len);
433         inFile->Read(stringContents.data(), len);
434 
435         inner(stringContents, len);
436 
437         auto dataToWrite = stringContents.data();
438         auto lenToWrite = stringContents.length();
439         fileManager.DeleteFile(fileUri);
440         auto outFile = fileManager.CreateFile(fileUri);
441         outFile->Write(dataToWrite, lenToWrite);
442     }
443 }
444 
InsertIntoString(BASE_NS::string & search,BASE_NS::string & insertion,InsertType type,BASE_NS::string & stringContents,size_t len)445 void InsertIntoString(
446     BASE_NS::string& search, BASE_NS::string& insertion, InsertType type, BASE_NS::string& stringContents, size_t len)
447 {
448     auto pos = stringContents.find(search, 0UL);
449     if (pos != BASE_NS::string::npos) {
450         if (type == InsertType::TAG) {
451             auto nlPos = stringContents.find("\n", pos);
452             if (nlPos == BASE_NS::string::npos) {
453                 nlPos = len - 1;
454             }
455             stringContents.insert(nlPos + 1, (insertion + "\r\n").data());
456         } else if (type == InsertType::SIGNATURE) {
457             auto bPos = stringContents.find('{', pos);
458             if (bPos == BASE_NS::string::npos) {
459                 return;
460             }
461             size_t depth{ 0 };
462             auto endPos = BASE_NS::string::npos;
463             while (bPos < len) {
464                 const auto& ch = stringContents[bPos];
465                 if (ch == '}') {
466                     depth--;
467                     if (depth == 0) {
468                         endPos = bPos;
469                         break;
470                     }
471                 } else if (ch == '{') {
472                     depth++;
473                 }
474                 bPos++;
475             }
476             if (endPos != BASE_NS::string::npos) {
477                 if (endPos == len - 1) {
478                     stringContents.insert(len - 1, ("\r\n" + insertion + "\r\n").data());
479                 } else {
480                     stringContents.insert(endPos, (insertion + "\r\n").data());
481                 }
482             }
483         }
484     }
485 }
486 
InsertInFile(CORE_NS::IFileManager & fileManager,BASE_NS::string_view fileUri,BASE_NS::string search,BASE_NS::string insertion,InsertType type)487 void InsertInFile(CORE_NS::IFileManager& fileManager, BASE_NS::string_view fileUri, BASE_NS::string search,
488     BASE_NS::string insertion, InsertType type)
489 {
490     auto func = [&search, &insertion, &type](BASE_NS::string& stringContents, size_t len) {
491         InsertIntoString(search, insertion, type, stringContents, len);
492     };
493     InsertInFileBoilerplate(fileManager, fileUri, func);
494 }
495 
InsertInFile(CORE_NS::IFileManager & fileManager,BASE_NS::string_view fileUri,BASE_NS::vector<Insertion> insertions)496 void InsertInFile(
497     CORE_NS::IFileManager& fileManager, BASE_NS::string_view fileUri, BASE_NS::vector<Insertion> insertions)
498 {
499     auto func = [&insertions](BASE_NS::string& stringContents, size_t len) {
500         for (auto& ins : insertions) {
501             InsertIntoString(ins.searchStr, ins.insertStr, ins.type, stringContents, len);
502         }
503     };
504     InsertInFileBoilerplate(fileManager, fileUri, func);
505 };
506 
ReplaceInString(BASE_NS::string & string,const BASE_NS::string & target,const BASE_NS::string & replacement)507 void ReplaceInString(BASE_NS::string& string, const BASE_NS::string& target, const BASE_NS::string& replacement)
508 {
509     auto pos = string.find(target, 0UL);
510     while (pos != BASE_NS::string::npos) {
511         string = string.replace(string.begin() + static_cast<int64_t>(pos), string.begin() +
512             static_cast<int64_t>(pos + target.size()), replacement);
513         pos += replacement.size();
514         pos = string.find(target, pos);
515     }
516 }
517 
518 } // namespace IoUtil
519 
520 UTIL_END_NAMESPACE()
521