1 /*
2  * Copyright (c) 2023 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 #include "read_lines.h"
16 
17 #include <unistd.h>
18 
19 #include "class_readeriterator/readeriterator_entity.h"
20 #include "class_readeriterator/readeriterator_n_exporter.h"
21 #include "common_func.h"
22 #include "file_utils.h"
23 #include "filemgmt_libhilog.h"
24 #include "rust_file.h"
25 
26 namespace OHOS {
27 namespace FileManagement {
28 namespace ModuleFileIO {
29 using namespace std;
30 using namespace OHOS::FileManagement::LibN;
31 
CheckOptionArg(napi_env env,napi_value argOption)32 static int CheckOptionArg(napi_env env, napi_value argOption)
33 {
34     NVal option(env, argOption);
35     if (option.HasProp("encoding")) {
36         auto [succ, encoding, ignore] = option.GetProp("encoding").ToUTF8String("utf-8");
37         string_view encodingStr(encoding.get());
38         if (!succ || encodingStr != "utf-8") {
39             return EINVAL;
40         }
41     }
42 
43     return ERRNO_NOERR;
44 }
45 
GetFileSize(const string & path,int64_t & offset)46 static int GetFileSize(const string &path, int64_t &offset)
47 {
48     std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> stat_req = {
49         new (std::nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
50     if (!stat_req) {
51         HILOGE("Failed to request heap memory.");
52         return ENOMEM;
53     }
54 
55     int ret = uv_fs_stat(nullptr, stat_req.get(), path.c_str(), nullptr);
56     if (ret < 0) {
57         HILOGE("Failed to get file stat by path");
58         return ret;
59     }
60 
61     offset = static_cast<int64_t>(stat_req->statbuf.st_size);
62     return ERRNO_NOERR;
63 }
64 
InstantiateReaderIterator(napi_env env,void * iterator,int64_t offset,bool async=false)65 static NVal InstantiateReaderIterator(napi_env env, void *iterator, int64_t offset, bool async = false)
66 {
67     if (iterator == nullptr) {
68         HILOGE("Invalid argument iterator");
69         if (async) {
70             return {env, NError(EINVAL).GetNapiErr(env)};
71         }
72         NError(EINVAL).ThrowErr(env);
73         return NVal();
74     }
75 
76     napi_value objReaderIterator = NClass::InstantiateClass(env, ReaderIteratorNExporter::className_, {});
77     if (!objReaderIterator) {
78         HILOGE("Failed to instantiate class ReaderIterator");
79         if (async) {
80             return {env, NError(UNKROWN_ERR).GetNapiErr(env)};
81         }
82         NError(UNKROWN_ERR).ThrowErr(env);
83         return NVal();
84     }
85 
86     auto readerIteratorEntity = NClass::GetEntityOf<ReaderIteratorEntity>(env, objReaderIterator);
87     if (!readerIteratorEntity) {
88         HILOGE("Failed to get readerIteratorEntity");
89         if (async) {
90             return {env, NError(UNKROWN_ERR).GetNapiErr(env)};
91         }
92         NError(UNKROWN_ERR).ThrowErr(env);
93         return NVal();
94     }
95 
96     readerIteratorEntity->iterator = iterator;
97     readerIteratorEntity->offset = offset;
98     return { env, objReaderIterator };
99 }
100 
101 struct ReaderIteratorArg {
102     void *iterator = nullptr;
103     int64_t offset = 0;
104 };
105 
AsyncExec(ReaderIteratorArg & readerIterator,const string & pathStr)106 static NError AsyncExec(ReaderIteratorArg &readerIterator, const string &pathStr)
107 {
108     readerIterator.iterator = ::ReaderIterator(pathStr.c_str());
109     if (readerIterator.iterator == nullptr) {
110         HILOGE("Failed to read lines of the file, error: %{public}d", errno);
111         return NError(errno);
112     }
113     int ret = GetFileSize(pathStr, readerIterator.offset);
114     if (ret < 0) {
115         HILOGE("Failed to get size of the file");
116         return NError(ret);
117     }
118 
119     return NError(ERRNO_NOERR);
120 }
121 
Async(napi_env env,napi_callback_info info)122 napi_value ReadLines::Async(napi_env env, napi_callback_info info)
123 {
124     NFuncArg funcArg(env, info);
125     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
126         HILOGE("Number of arguments unmatched");
127         NError(EINVAL).ThrowErr(env);
128         return nullptr;
129     }
130 
131     auto [succPath, path, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
132     if (!succPath) {
133         HILOGE("Invalid path from JS first argument");
134         NError(EINVAL).ThrowErr(env);
135         return nullptr;
136     }
137 
138     if (funcArg.GetArgc() >= NARG_CNT::TWO) {
139         int ret = CheckOptionArg(env, funcArg[NARG_POS::SECOND]);
140         if (ret) {
141             HILOGE("Invalid option.encoding parameter");
142             NError(ret).ThrowErr(env);
143             return nullptr;
144         }
145     }
146 
147     auto arg = CreateSharedPtr<ReaderIteratorArg>();
148     if (arg == nullptr) {
149         HILOGE("Failed to request heap memory.");
150         NError(ENOMEM).ThrowErr(env);
151         return nullptr;
152     }
153     auto cbExec = [arg, pathStr = string(path.get())]() -> NError {
154         return AsyncExec(*arg, pathStr);
155     };
156 
157     auto cbCompl = [arg](napi_env env, NError err) -> NVal {
158         if (err) {
159             return { env, err.GetNapiErr(env) };
160         }
161         return InstantiateReaderIterator(env, arg->iterator, arg->offset, true);
162     };
163 
164     NVal thisVar(env, funcArg.GetThisVar());
165     if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
166         !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
167         return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_READLINES_NAME, cbExec, cbCompl).val_;
168     } else {
169         NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD)]);
170         return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_READLINES_NAME, cbExec, cbCompl).val_;
171     }
172 }
173 
Sync(napi_env env,napi_callback_info info)174 napi_value ReadLines::Sync(napi_env env, napi_callback_info info)
175 {
176     NFuncArg funcArg(env, info);
177     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
178         HILOGE("Number of arguments unmatched");
179         NError(EINVAL).ThrowErr(env);
180         return nullptr;
181     }
182 
183     auto [succPath, path, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
184     if (!succPath) {
185         HILOGE("Invalid path from JS first argument");
186         NError(EINVAL).ThrowErr(env);
187         return nullptr;
188     }
189 
190     if (funcArg.GetArgc() == NARG_CNT::TWO) {
191         int ret = CheckOptionArg(env, funcArg[NARG_POS::SECOND]);
192         if (ret) {
193             HILOGE("Invalid option.encoding parameter");
194             NError(ret).ThrowErr(env);
195             return nullptr;
196         }
197     }
198 
199     auto iterator = ::ReaderIterator(path.get());
200     if (iterator == nullptr) {
201         HILOGE("Failed to read lines of the file, error: %{public}d", errno);
202         NError(errno).ThrowErr(env);
203         return nullptr;
204     }
205 
206     int64_t offset = 0;
207     int ret = GetFileSize(path.get(), offset);
208     if (ret != 0) {
209         HILOGE("Failed to get size of the file");
210         return nullptr;
211     }
212 
213     return InstantiateReaderIterator(env, iterator, offset).val_;
214 }
215 
216 } // ModuleFileIO
217 } // FileManagement
218 } // OHOS