1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "bin/file.h" |
6 | |
7 | #include <stdio.h> |
8 | |
9 | #include "bin/builtin.h" |
10 | #include "bin/dartutils.h" |
11 | #include "bin/io_buffer.h" |
12 | #include "bin/namespace.h" |
13 | #include "bin/typed_data_utils.h" |
14 | #include "bin/utils.h" |
15 | #include "include/bin/dart_io_api.h" |
16 | #include "include/dart_api.h" |
17 | #include "include/dart_tools_api.h" |
18 | #include "platform/globals.h" |
19 | |
20 | namespace dart { |
21 | namespace bin { |
22 | |
23 | static const int kFileNativeFieldIndex = 0; |
24 | |
25 | #if !defined(PRODUCT) |
26 | static bool IsFile(Dart_Handle file_obj) { |
27 | Dart_Handle file_type = ThrowIfError( |
28 | DartUtils::GetDartType("dart:io" , "_RandomAccessFileOpsImpl" )); |
29 | bool isinstance = false; |
30 | ThrowIfError(Dart_ObjectIsType(file_obj, file_type, &isinstance)); |
31 | return isinstance; |
32 | } |
33 | #endif |
34 | |
35 | // The file pointer has been passed into Dart as an intptr_t and it is safe |
36 | // to pull it out of Dart as a 64-bit integer, cast it to an intptr_t and |
37 | // from there to a File pointer. |
38 | static File* GetFile(Dart_NativeArguments args) { |
39 | File* file; |
40 | Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
41 | DEBUG_ASSERT(IsFile(dart_this)); |
42 | Dart_Handle result = Dart_GetNativeInstanceField( |
43 | dart_this, kFileNativeFieldIndex, reinterpret_cast<intptr_t*>(&file)); |
44 | ASSERT(!Dart_IsError(result)); |
45 | if (file == NULL) { |
46 | Dart_PropagateError(Dart_NewUnhandledExceptionError( |
47 | DartUtils::NewInternalError("No native peer" ))); |
48 | } |
49 | return file; |
50 | } |
51 | |
52 | static void SetFile(Dart_Handle dart_this, intptr_t file_pointer) { |
53 | DEBUG_ASSERT(IsFile(dart_this)); |
54 | Dart_Handle result = Dart_SetNativeInstanceField( |
55 | dart_this, kFileNativeFieldIndex, file_pointer); |
56 | ThrowIfError(result); |
57 | } |
58 | |
59 | void FUNCTION_NAME(File_GetPointer)(Dart_NativeArguments args) { |
60 | File* file = GetFile(args); |
61 | // If the file is already closed, GetFile() will return NULL. |
62 | if (file != NULL) { |
63 | // Increment file's reference count. File_GetPointer() should only be called |
64 | // when we are about to send the File* to the IO Service. |
65 | file->Retain(); |
66 | } |
67 | intptr_t file_pointer = reinterpret_cast<intptr_t>(file); |
68 | Dart_SetIntegerReturnValue(args, file_pointer); |
69 | } |
70 | |
71 | static void ReleaseFile(void* isolate_callback_data, void* peer) { |
72 | File* file = reinterpret_cast<File*>(peer); |
73 | file->Release(); |
74 | } |
75 | |
76 | void FUNCTION_NAME(File_SetPointer)(Dart_NativeArguments args) { |
77 | Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
78 | intptr_t file_pointer = DartUtils::GetNativeIntptrArgument(args, 1); |
79 | File* file = reinterpret_cast<File*>(file_pointer); |
80 | Dart_FinalizableHandle handle = Dart_NewFinalizableHandle( |
81 | dart_this, reinterpret_cast<void*>(file), sizeof(*file), ReleaseFile); |
82 | file->SetFinalizableHandle(handle); |
83 | SetFile(dart_this, file_pointer); |
84 | } |
85 | |
86 | void FUNCTION_NAME(File_Open)(Dart_NativeArguments args) { |
87 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
88 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
89 | File* file = NULL; |
90 | OSError os_error; |
91 | { |
92 | TypedDataScope data(path_handle); |
93 | ASSERT(data.type() == Dart_TypedData_kUint8); |
94 | const char* filename = data.GetCString(); |
95 | |
96 | int64_t mode = DartUtils::GetNativeIntegerArgument(args, 2); |
97 | File::DartFileOpenMode dart_file_mode = |
98 | static_cast<File::DartFileOpenMode>(mode); |
99 | File::FileOpenMode file_mode = File::DartModeToFileMode(dart_file_mode); |
100 | // Check that the file exists before opening it only for |
101 | // reading. This is to prevent the opening of directories as |
102 | // files. Directories can be opened for reading using the posix |
103 | // 'open' call. |
104 | file = File::Open(namespc, filename, file_mode); |
105 | if (file == NULL) { |
106 | // Errors must be caught before TypedDataScope data is destroyed. |
107 | os_error.Reload(); |
108 | } |
109 | } |
110 | if (file != NULL) { |
111 | Dart_SetIntegerReturnValue(args, reinterpret_cast<intptr_t>(file)); |
112 | } else { |
113 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
114 | } |
115 | } |
116 | |
117 | void FUNCTION_NAME(File_Exists)(Dart_NativeArguments args) { |
118 | bool exists; |
119 | { |
120 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
121 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
122 | TypedDataScope data(path_handle); |
123 | ASSERT(data.type() == Dart_TypedData_kUint8); |
124 | const char* filename = data.GetCString(); |
125 | exists = File::Exists(namespc, filename); |
126 | } |
127 | Dart_SetBooleanReturnValue(args, exists); |
128 | } |
129 | |
130 | void FUNCTION_NAME(File_Close)(Dart_NativeArguments args) { |
131 | // TODO(zra): The bots are hitting a crash in this function, so we include |
132 | // some checks here that are normally only in a Debug build. When the crash |
133 | // is gone, this can go back to using GetFile and SetFile. |
134 | Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
135 | #if !defined(PRODUCT) |
136 | if (!IsFile(dart_this)) { |
137 | Dart_PropagateError(DartUtils::NewInternalError( |
138 | "File_Close expects the reciever to be a _RandomAccessFileOpsImpl." )); |
139 | } |
140 | #endif |
141 | File* file; |
142 | ThrowIfError(Dart_GetNativeInstanceField(dart_this, kFileNativeFieldIndex, |
143 | reinterpret_cast<intptr_t*>(&file))); |
144 | if (file == NULL) { |
145 | Dart_SetIntegerReturnValue(args, -1); |
146 | return; |
147 | } |
148 | file->Close(); |
149 | file->DeleteFinalizableHandle(Dart_CurrentIsolate(), dart_this); |
150 | file->Release(); |
151 | |
152 | ThrowIfError( |
153 | Dart_SetNativeInstanceField(dart_this, kFileNativeFieldIndex, 0)); |
154 | Dart_SetIntegerReturnValue(args, 0); |
155 | } |
156 | |
157 | void FUNCTION_NAME(File_ReadByte)(Dart_NativeArguments args) { |
158 | File* file = GetFile(args); |
159 | ASSERT(file != NULL); |
160 | uint8_t buffer; |
161 | int64_t bytes_read = file->Read(reinterpret_cast<void*>(&buffer), 1); |
162 | if (bytes_read == 1) { |
163 | Dart_SetIntegerReturnValue(args, buffer); |
164 | } else if (bytes_read == 0) { |
165 | Dart_SetIntegerReturnValue(args, -1); |
166 | } else { |
167 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
168 | } |
169 | } |
170 | |
171 | void FUNCTION_NAME(File_WriteByte)(Dart_NativeArguments args) { |
172 | File* file = GetFile(args); |
173 | ASSERT(file != NULL); |
174 | int64_t byte = 0; |
175 | if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &byte)) { |
176 | uint8_t buffer = static_cast<uint8_t>(byte & 0xff); |
177 | bool success = file->WriteFully(reinterpret_cast<void*>(&buffer), 1); |
178 | if (success) { |
179 | Dart_SetIntegerReturnValue(args, 1); |
180 | } else { |
181 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
182 | } |
183 | } else { |
184 | OSError os_error(-1, "Invalid argument" , OSError::kUnknown); |
185 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
186 | } |
187 | } |
188 | |
189 | void FUNCTION_NAME(File_Read)(Dart_NativeArguments args) { |
190 | File* file = GetFile(args); |
191 | ASSERT(file != NULL); |
192 | Dart_Handle length_object = Dart_GetNativeArgument(args, 1); |
193 | int64_t length = 0; |
194 | if (!DartUtils::GetInt64Value(length_object, &length) || (length < 0)) { |
195 | OSError os_error(-1, "Invalid argument" , OSError::kUnknown); |
196 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
197 | return; |
198 | } |
199 | uint8_t* buffer = NULL; |
200 | Dart_Handle external_array = IOBuffer::Allocate(length, &buffer); |
201 | if (Dart_IsNull(external_array)) { |
202 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
203 | return; |
204 | } |
205 | int64_t bytes_read = file->Read(reinterpret_cast<void*>(buffer), length); |
206 | if (bytes_read < 0) { |
207 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
208 | return; |
209 | } |
210 | if (bytes_read < length) { |
211 | const int kNumArgs = 3; |
212 | Dart_Handle dart_args[kNumArgs]; |
213 | dart_args[0] = external_array; |
214 | dart_args[1] = Dart_NewInteger(0); |
215 | dart_args[2] = Dart_NewInteger(bytes_read); |
216 | // TODO(sgjesse): Cache the _makeUint8ListView function somewhere. |
217 | Dart_Handle io_lib = Dart_LookupLibrary(DartUtils::NewString("dart:io" )); |
218 | ThrowIfError(io_lib); |
219 | Dart_Handle array_view = |
220 | Dart_Invoke(io_lib, DartUtils::NewString("_makeUint8ListView" ), |
221 | kNumArgs, dart_args); |
222 | Dart_SetReturnValue(args, array_view); |
223 | } else { |
224 | Dart_SetReturnValue(args, external_array); |
225 | } |
226 | } |
227 | |
228 | void FUNCTION_NAME(File_ReadInto)(Dart_NativeArguments args) { |
229 | File* file = GetFile(args); |
230 | ASSERT(file != NULL); |
231 | Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1); |
232 | ASSERT(Dart_IsList(buffer_obj)); |
233 | // start and end arguments are checked in Dart code to be |
234 | // integers and have the property that end <= |
235 | // list.length. Therefore, it is safe to extract their value as |
236 | // intptr_t. |
237 | intptr_t start = DartUtils::GetNativeIntptrArgument(args, 2); |
238 | intptr_t end = DartUtils::GetNativeIntptrArgument(args, 3); |
239 | intptr_t length = end - start; |
240 | intptr_t array_len = 0; |
241 | Dart_Handle result = Dart_ListLength(buffer_obj, &array_len); |
242 | ThrowIfError(result); |
243 | ASSERT(end <= array_len); |
244 | uint8_t* buffer = Dart_ScopeAllocate(length); |
245 | int64_t bytes_read = file->Read(reinterpret_cast<void*>(buffer), length); |
246 | if (bytes_read >= 0) { |
247 | result = Dart_ListSetAsBytes(buffer_obj, start, buffer, bytes_read); |
248 | if (Dart_IsError(result)) { |
249 | Dart_SetReturnValue(args, result); |
250 | } else { |
251 | Dart_SetIntegerReturnValue(args, bytes_read); |
252 | } |
253 | } else { |
254 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
255 | } |
256 | } |
257 | |
258 | void FUNCTION_NAME(File_WriteFrom)(Dart_NativeArguments args) { |
259 | File* file = GetFile(args); |
260 | ASSERT(file != NULL); |
261 | |
262 | Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1); |
263 | |
264 | // Offset and length arguments are checked in Dart code to be |
265 | // integers and have the property that (offset + length) <= |
266 | // list.length. Therefore, it is safe to extract their value as |
267 | // intptr_t. |
268 | intptr_t start = DartUtils::GetNativeIntptrArgument(args, 2); |
269 | intptr_t end = DartUtils::GetNativeIntptrArgument(args, 3); |
270 | |
271 | // The buffer object passed in has to be an Int8List or Uint8List object. |
272 | // Acquire a direct pointer to the data area of the buffer object. |
273 | Dart_TypedData_Type type; |
274 | intptr_t length = end - start; |
275 | intptr_t buffer_len = 0; |
276 | void* buffer = NULL; |
277 | Dart_Handle result = |
278 | Dart_TypedDataAcquireData(buffer_obj, &type, &buffer, &buffer_len); |
279 | ThrowIfError(result); |
280 | |
281 | ASSERT(type == Dart_TypedData_kUint8 || type == Dart_TypedData_kInt8); |
282 | ASSERT(end <= buffer_len); |
283 | ASSERT(buffer != NULL); |
284 | |
285 | // Write all the data out into the file. |
286 | char* byte_buffer = reinterpret_cast<char*>(buffer); |
287 | bool success = file->WriteFully(byte_buffer + start, length); |
288 | |
289 | // Release the direct pointer acquired above. |
290 | ThrowIfError(Dart_TypedDataReleaseData(buffer_obj)); |
291 | if (!success) { |
292 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
293 | } else { |
294 | Dart_SetReturnValue(args, Dart_Null()); |
295 | } |
296 | } |
297 | |
298 | void FUNCTION_NAME(File_Position)(Dart_NativeArguments args) { |
299 | File* file = GetFile(args); |
300 | ASSERT(file != NULL); |
301 | intptr_t return_value = file->Position(); |
302 | if (return_value >= 0) { |
303 | Dart_SetIntegerReturnValue(args, return_value); |
304 | } else { |
305 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
306 | } |
307 | } |
308 | |
309 | void FUNCTION_NAME(File_SetPosition)(Dart_NativeArguments args) { |
310 | File* file = GetFile(args); |
311 | ASSERT(file != NULL); |
312 | int64_t position = 0; |
313 | if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &position)) { |
314 | if (file->SetPosition(position)) { |
315 | Dart_SetBooleanReturnValue(args, true); |
316 | } else { |
317 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
318 | } |
319 | } else { |
320 | OSError os_error(-1, "Invalid argument" , OSError::kUnknown); |
321 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
322 | } |
323 | } |
324 | |
325 | void FUNCTION_NAME(File_Truncate)(Dart_NativeArguments args) { |
326 | File* file = GetFile(args); |
327 | ASSERT(file != NULL); |
328 | int64_t length = 0; |
329 | if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &length)) { |
330 | if (file->Truncate(length)) { |
331 | Dart_SetBooleanReturnValue(args, true); |
332 | } else { |
333 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
334 | } |
335 | } else { |
336 | OSError os_error(-1, "Invalid argument" , OSError::kUnknown); |
337 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
338 | } |
339 | } |
340 | |
341 | void FUNCTION_NAME(File_Length)(Dart_NativeArguments args) { |
342 | File* file = GetFile(args); |
343 | ASSERT(file != NULL); |
344 | int64_t return_value = file->Length(); |
345 | if (return_value >= 0) { |
346 | Dart_SetIntegerReturnValue(args, return_value); |
347 | } else { |
348 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
349 | } |
350 | } |
351 | |
352 | void FUNCTION_NAME(File_LengthFromPath)(Dart_NativeArguments args) { |
353 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
354 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
355 | int64_t return_value; |
356 | OSError os_error; |
357 | { |
358 | TypedDataScope data(path_handle); |
359 | ASSERT(data.type() == Dart_TypedData_kUint8); |
360 | const char* path = data.GetCString(); |
361 | return_value = File::LengthFromPath(namespc, path); |
362 | if (return_value < 0) { |
363 | // Errors must be caught before TypedDataScope data is destroyed. |
364 | os_error.Reload(); |
365 | } |
366 | } |
367 | if (return_value >= 0) { |
368 | Dart_SetIntegerReturnValue(args, return_value); |
369 | } else { |
370 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
371 | } |
372 | } |
373 | |
374 | void FUNCTION_NAME(File_LastModified)(Dart_NativeArguments args) { |
375 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
376 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
377 | int64_t return_value; |
378 | OSError os_error; |
379 | { |
380 | TypedDataScope data(path_handle); |
381 | ASSERT(data.type() == Dart_TypedData_kUint8); |
382 | const char* raw_name = data.GetCString(); |
383 | return_value = File::LastModified(namespc, raw_name); |
384 | if (return_value < 0) { |
385 | // Errors must be caught before TypedDataScope data is destroyed. |
386 | os_error.Reload(); |
387 | } |
388 | } |
389 | if (return_value >= 0) { |
390 | Dart_SetIntegerReturnValue(args, return_value * kMillisecondsPerSecond); |
391 | } else { |
392 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
393 | } |
394 | } |
395 | |
396 | void FUNCTION_NAME(File_SetLastModified)(Dart_NativeArguments args) { |
397 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
398 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
399 | int64_t millis; |
400 | if (!DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 2), &millis)) { |
401 | Dart_ThrowException(DartUtils::NewDartArgumentError( |
402 | "The second argument must be a 64-bit int." )); |
403 | } |
404 | bool result; |
405 | OSError os_error; |
406 | { |
407 | TypedDataScope data(path_handle); |
408 | ASSERT(data.type() == Dart_TypedData_kUint8); |
409 | const char* name = data.GetCString(); |
410 | result = File::SetLastModified(namespc, name, millis); |
411 | if (!result) { |
412 | // Errors must be caught before TypedDataScope data is destroyed. |
413 | os_error.Reload(); |
414 | } |
415 | } |
416 | if (!result) { |
417 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
418 | } |
419 | } |
420 | |
421 | void FUNCTION_NAME(File_LastAccessed)(Dart_NativeArguments args) { |
422 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
423 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
424 | int64_t return_value; |
425 | OSError os_error; |
426 | { |
427 | TypedDataScope data(path_handle); |
428 | ASSERT(data.type() == Dart_TypedData_kUint8); |
429 | const char* name = data.GetCString(); |
430 | return_value = File::LastAccessed(namespc, name); |
431 | if (return_value < 0) { |
432 | // Errors must be caught before TypedDataScope data is destroyed. |
433 | os_error.Reload(); |
434 | } |
435 | } |
436 | if (return_value >= 0) { |
437 | Dart_SetIntegerReturnValue(args, return_value * kMillisecondsPerSecond); |
438 | } else { |
439 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
440 | } |
441 | } |
442 | |
443 | void FUNCTION_NAME(File_SetLastAccessed)(Dart_NativeArguments args) { |
444 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
445 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
446 | int64_t millis; |
447 | if (!DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 2), &millis)) { |
448 | Dart_ThrowException(DartUtils::NewDartArgumentError( |
449 | "The second argument must be a 64-bit int." )); |
450 | } |
451 | bool result; |
452 | OSError os_error; |
453 | { |
454 | TypedDataScope data(path_handle); |
455 | ASSERT(data.type() == Dart_TypedData_kUint8); |
456 | const char* name = data.GetCString(); |
457 | result = File::SetLastAccessed(namespc, name, millis); |
458 | if (!result) { |
459 | // Errors must be caught before TypedDataScope data is destroyed. |
460 | os_error.Reload(); |
461 | } |
462 | } |
463 | if (!result) { |
464 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
465 | } |
466 | } |
467 | |
468 | void FUNCTION_NAME(File_Flush)(Dart_NativeArguments args) { |
469 | File* file = GetFile(args); |
470 | ASSERT(file != NULL); |
471 | if (file->Flush()) { |
472 | Dart_SetBooleanReturnValue(args, true); |
473 | } else { |
474 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
475 | } |
476 | } |
477 | |
478 | void FUNCTION_NAME(File_Lock)(Dart_NativeArguments args) { |
479 | File* file = GetFile(args); |
480 | ASSERT(file != NULL); |
481 | int64_t lock; |
482 | int64_t start; |
483 | int64_t end; |
484 | if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &lock) && |
485 | DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 2), &start) && |
486 | DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 3), &end)) { |
487 | if ((lock >= File::kLockMin) && (lock <= File::kLockMax) && (start >= 0) && |
488 | (end == -1 || end > start)) { |
489 | if (file->Lock(static_cast<File::LockType>(lock), start, end)) { |
490 | Dart_SetBooleanReturnValue(args, true); |
491 | } else { |
492 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
493 | } |
494 | return; |
495 | } |
496 | } |
497 | OSError os_error(-1, "Invalid argument" , OSError::kUnknown); |
498 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
499 | } |
500 | |
501 | void FUNCTION_NAME(File_Create)(Dart_NativeArguments args) { |
502 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
503 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
504 | bool result; |
505 | OSError os_error; |
506 | { |
507 | TypedDataScope data(path_handle); |
508 | ASSERT(data.type() == Dart_TypedData_kUint8); |
509 | const char* path = data.GetCString(); |
510 | result = File::Create(namespc, path); |
511 | if (!result) { |
512 | // Errors must be caught before TypedDataScope data is destroyed. |
513 | os_error.Reload(); |
514 | } |
515 | } |
516 | if (result) { |
517 | Dart_SetBooleanReturnValue(args, result); |
518 | } else { |
519 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
520 | } |
521 | } |
522 | |
523 | void FUNCTION_NAME(File_CreateLink)(Dart_NativeArguments args) { |
524 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
525 | bool result; |
526 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
527 | OSError os_error; |
528 | { |
529 | TypedDataScope data(path_handle); |
530 | ASSERT(data.type() == Dart_TypedData_kUint8); |
531 | const char* name = data.GetCString(); |
532 | const char* target = DartUtils::GetNativeStringArgument(args, 2); |
533 | result = File::CreateLink(namespc, name, target); |
534 | if (!result) { |
535 | // Errors must be caught before TypedDataScope data is destroyed. |
536 | os_error.Reload(); |
537 | } |
538 | } |
539 | if (!result) { |
540 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
541 | } |
542 | } |
543 | |
544 | void FUNCTION_NAME(File_LinkTarget)(Dart_NativeArguments args) { |
545 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
546 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
547 | const char* target = NULL; |
548 | OSError os_error; |
549 | { |
550 | TypedDataScope data(path_handle); |
551 | ASSERT(data.type() == Dart_TypedData_kUint8); |
552 | const char* name = data.GetCString(); |
553 | target = File::LinkTarget(namespc, name); |
554 | if (target == NULL) { |
555 | // Errors must be caught before TypedDataScope data is destroyed. |
556 | os_error.Reload(); |
557 | } |
558 | } |
559 | if (target == NULL) { |
560 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
561 | } else { |
562 | Dart_Handle str = ThrowIfError(DartUtils::NewString(target)); |
563 | Dart_SetReturnValue(args, str); |
564 | } |
565 | } |
566 | |
567 | void FUNCTION_NAME(File_Delete)(Dart_NativeArguments args) { |
568 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
569 | bool result; |
570 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
571 | OSError os_error; |
572 | { |
573 | TypedDataScope data(path_handle); |
574 | ASSERT(data.type() == Dart_TypedData_kUint8); |
575 | const char* path = data.GetCString(); |
576 | result = File::Delete(namespc, path); |
577 | if (!result) { |
578 | // Errors must be caught before TypedDataScope data is destroyed. |
579 | os_error.Reload(); |
580 | } |
581 | } |
582 | if (result) { |
583 | Dart_SetBooleanReturnValue(args, result); |
584 | } else { |
585 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
586 | } |
587 | } |
588 | |
589 | void FUNCTION_NAME(File_DeleteLink)(Dart_NativeArguments args) { |
590 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
591 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
592 | bool result; |
593 | OSError os_error; |
594 | { |
595 | TypedDataScope data(path_handle); |
596 | ASSERT(data.type() == Dart_TypedData_kUint8); |
597 | const char* path = data.GetCString(); |
598 | result = File::DeleteLink(namespc, path); |
599 | if (!result) { |
600 | // Errors must be caught before TypedDataScope data is destroyed. |
601 | os_error.Reload(); |
602 | } |
603 | } |
604 | if (result) { |
605 | Dart_SetBooleanReturnValue(args, result); |
606 | } else { |
607 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
608 | } |
609 | } |
610 | |
611 | void FUNCTION_NAME(File_Rename)(Dart_NativeArguments args) { |
612 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
613 | Dart_Handle old_path_handle = Dart_GetNativeArgument(args, 1); |
614 | bool result; |
615 | OSError os_error; |
616 | { |
617 | TypedDataScope old_path_data(old_path_handle); |
618 | ASSERT(old_path_data.type() == Dart_TypedData_kUint8); |
619 | const char* old_path = old_path_data.GetCString(); |
620 | const char* new_path = DartUtils::GetNativeStringArgument(args, 2); |
621 | result = File::Rename(namespc, old_path, new_path); |
622 | if (!result) { |
623 | // Errors must be caught before TypedDataScope data is destroyed. |
624 | os_error.Reload(); |
625 | } |
626 | } |
627 | if (result) { |
628 | Dart_SetBooleanReturnValue(args, result); |
629 | } else { |
630 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
631 | } |
632 | } |
633 | |
634 | void FUNCTION_NAME(File_RenameLink)(Dart_NativeArguments args) { |
635 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
636 | Dart_Handle old_path_handle = Dart_GetNativeArgument(args, 1); |
637 | bool result; |
638 | OSError os_error; |
639 | { |
640 | TypedDataScope old_path_data(old_path_handle); |
641 | ASSERT(old_path_data.type() == Dart_TypedData_kUint8); |
642 | const char* old_path = old_path_data.GetCString(); |
643 | const char* new_path = DartUtils::GetNativeStringArgument(args, 2); |
644 | result = File::RenameLink(namespc, old_path, new_path); |
645 | if (!result) { |
646 | // Errors must be caught before TypedDataScope data is destroyed. |
647 | os_error.Reload(); |
648 | } |
649 | } |
650 | if (result) { |
651 | Dart_SetBooleanReturnValue(args, result); |
652 | } else { |
653 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
654 | } |
655 | } |
656 | |
657 | void FUNCTION_NAME(File_Copy)(Dart_NativeArguments args) { |
658 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
659 | Dart_Handle old_path_handle = Dart_GetNativeArgument(args, 1); |
660 | bool result; |
661 | OSError os_error; |
662 | { |
663 | TypedDataScope old_path_data(old_path_handle); |
664 | ASSERT(old_path_data.type() == Dart_TypedData_kUint8); |
665 | const char* old_path = old_path_data.GetCString(); |
666 | const char* new_path = DartUtils::GetNativeStringArgument(args, 2); |
667 | result = File::Copy(namespc, old_path, new_path); |
668 | if (!result) { |
669 | // Errors must be caught before TypedDataScope data is destroyed. |
670 | os_error.Reload(); |
671 | } |
672 | } |
673 | if (result) { |
674 | Dart_SetBooleanReturnValue(args, result); |
675 | } else { |
676 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
677 | } |
678 | } |
679 | |
680 | void FUNCTION_NAME(File_ResolveSymbolicLinks)(Dart_NativeArguments args) { |
681 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
682 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
683 | const char* path = NULL; |
684 | OSError os_error; |
685 | { |
686 | TypedDataScope data(path_handle); |
687 | ASSERT(data.type() == Dart_TypedData_kUint8); |
688 | const char* str = data.GetCString(); |
689 | path = File::GetCanonicalPath(namespc, str); |
690 | if (path == NULL) { |
691 | // Errors must be caught before TypedDataScope data is destroyed. |
692 | os_error.Reload(); |
693 | } |
694 | } |
695 | if (path != NULL) { |
696 | Dart_Handle str = ThrowIfError(DartUtils::NewString(path)); |
697 | Dart_SetReturnValue(args, str); |
698 | } else { |
699 | Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
700 | } |
701 | } |
702 | |
703 | void FUNCTION_NAME(File_OpenStdio)(Dart_NativeArguments args) { |
704 | const int64_t fd = DartUtils::GetNativeIntegerArgument(args, 0); |
705 | File* file = File::OpenStdio(static_cast<int>(fd)); |
706 | Dart_SetIntegerReturnValue(args, reinterpret_cast<intptr_t>(file)); |
707 | } |
708 | |
709 | void FUNCTION_NAME(File_GetStdioHandleType)(Dart_NativeArguments args) { |
710 | int64_t fd = DartUtils::GetNativeIntegerArgument(args, 0); |
711 | ASSERT((fd == STDIN_FILENO) || (fd == STDOUT_FILENO) || |
712 | (fd == STDERR_FILENO)); |
713 | File::StdioHandleType type = File::GetStdioHandleType(static_cast<int>(fd)); |
714 | if (type == File::StdioHandleType::kTypeError) { |
715 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
716 | } else { |
717 | Dart_SetIntegerReturnValue(args, type); |
718 | } |
719 | } |
720 | |
721 | void FUNCTION_NAME(File_GetType)(Dart_NativeArguments args) { |
722 | File::Type type; |
723 | { |
724 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
725 | Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); |
726 | TypedDataScope data(path_handle); |
727 | ASSERT(data.type() == Dart_TypedData_kUint8); |
728 | const char* path = data.GetCString(); |
729 | bool follow_links = DartUtils::GetNativeBooleanArgument(args, 2); |
730 | type = File::GetType(namespc, path, follow_links); |
731 | } |
732 | Dart_SetIntegerReturnValue(args, static_cast<int>(type)); |
733 | } |
734 | |
735 | void FUNCTION_NAME(File_Stat)(Dart_NativeArguments args) { |
736 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
737 | const char* path = DartUtils::GetNativeStringArgument(args, 1); |
738 | |
739 | int64_t stat_data[File::kStatSize]; |
740 | File::Stat(namespc, path, stat_data); |
741 | if (stat_data[File::kType] == File::kDoesNotExist) { |
742 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
743 | return; |
744 | } |
745 | Dart_Handle returned_data = |
746 | Dart_NewTypedData(Dart_TypedData_kInt64, File::kStatSize); |
747 | ThrowIfError(returned_data); |
748 | Dart_TypedData_Type data_type_unused; |
749 | void* data_location; |
750 | intptr_t data_length_unused; |
751 | Dart_Handle status = Dart_TypedDataAcquireData( |
752 | returned_data, &data_type_unused, &data_location, &data_length_unused); |
753 | ThrowIfError(status); |
754 | memmove(data_location, stat_data, File::kStatSize * sizeof(int64_t)); |
755 | status = Dart_TypedDataReleaseData(returned_data); |
756 | ThrowIfError(status); |
757 | Dart_SetReturnValue(args, returned_data); |
758 | } |
759 | |
760 | void FUNCTION_NAME(File_AreIdentical)(Dart_NativeArguments args) { |
761 | Namespace* namespc = Namespace::GetNamespace(args, 0); |
762 | const char* path_1 = DartUtils::GetNativeStringArgument(args, 1); |
763 | const char* path_2 = DartUtils::GetNativeStringArgument(args, 2); |
764 | File::Identical result = File::AreIdentical(namespc, path_1, namespc, path_2); |
765 | if (result == File::kError) { |
766 | Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
767 | } else { |
768 | Dart_SetBooleanReturnValue(args, result == File::kIdentical); |
769 | } |
770 | } |
771 | |
772 | #define IS_SEPARATOR(c) ((c) == '/' || (c) == 0) |
773 | |
774 | // Checks that if we increment this index forward, we'll still have enough space |
775 | // for a null terminator within PATH_MAX bytes. |
776 | #define CHECK_CAN_INCREMENT(i) \ |
777 | if ((i) + 1 >= outlen) { \ |
778 | return -1; \ |
779 | } |
780 | |
781 | intptr_t File::CleanUnixPath(const char* in, char* out, intptr_t outlen) { |
782 | if (in[0] == 0) { |
783 | snprintf(out, outlen, "." ); |
784 | return 1; |
785 | } |
786 | |
787 | const bool rooted = (in[0] == '/'); |
788 | intptr_t in_index = 0; // Index of the next byte to read. |
789 | intptr_t out_index = 0; // Index of the next byte to write. |
790 | |
791 | if (rooted) { |
792 | out[out_index++] = '/'; |
793 | in_index++; |
794 | } |
795 | // The output index at which '..' cannot be cleaned further. |
796 | intptr_t dotdot = out_index; |
797 | |
798 | while (in[in_index] != 0) { |
799 | if (in[in_index] == '/') { |
800 | // 1. Reduce multiple slashes to a single slash. |
801 | CHECK_CAN_INCREMENT(in_index); |
802 | in_index++; |
803 | } else if ((in[in_index] == '.') && IS_SEPARATOR(in[in_index + 1])) { |
804 | // 2. Eliminate . path name elements (the current directory). |
805 | CHECK_CAN_INCREMENT(in_index); |
806 | in_index++; |
807 | } else if ((in[in_index] == '.') && (in[in_index + 1] == '.') && |
808 | IS_SEPARATOR(in[in_index + 2])) { |
809 | CHECK_CAN_INCREMENT(in_index + 1); |
810 | in_index += 2; |
811 | if (out_index > dotdot) { |
812 | // 3. Eliminate .. path elements (the parent directory) and the element |
813 | // that precedes them. |
814 | out_index--; |
815 | while ((out_index > dotdot) && (out[out_index] != '/')) { |
816 | out_index--; |
817 | } |
818 | } else if (rooted) { |
819 | // 4. Eliminate .. elements that begin a rooted path, that is, replace |
820 | // /.. by / at the beginning of a path. |
821 | continue; |
822 | } else if (!rooted) { |
823 | if (out_index > 0) { |
824 | out[out_index++] = '/'; |
825 | } |
826 | // 5. Leave intact .. elements that begin a non-rooted path. |
827 | out[out_index++] = '.'; |
828 | out[out_index++] = '.'; |
829 | dotdot = out_index; |
830 | } |
831 | } else { |
832 | if ((rooted && out_index != 1) || (!rooted && out_index != 0)) { |
833 | // Add '/' before normal path component, for non-root components. |
834 | out[out_index++] = '/'; |
835 | } |
836 | |
837 | while (!IS_SEPARATOR(in[in_index])) { |
838 | CHECK_CAN_INCREMENT(in_index); |
839 | out[out_index++] = in[in_index++]; |
840 | } |
841 | } |
842 | } |
843 | |
844 | if (out_index == 0) { |
845 | snprintf(out, outlen, "." ); |
846 | return 1; |
847 | } |
848 | |
849 | // Append null character. |
850 | out[out_index] = 0; |
851 | return out_index; |
852 | } |
853 | |
854 | static int64_t CObjectInt32OrInt64ToInt64(CObject* cobject) { |
855 | ASSERT(cobject->IsInt32OrInt64()); |
856 | int64_t result; |
857 | if (cobject->IsInt32()) { |
858 | CObjectInt32 value(cobject); |
859 | result = value.Value(); |
860 | } else { |
861 | CObjectInt64 value(cobject); |
862 | result = value.Value(); |
863 | } |
864 | return result; |
865 | } |
866 | |
867 | static File* CObjectToFilePointer(CObject* cobject) { |
868 | CObjectIntptr value(cobject); |
869 | return reinterpret_cast<File*>(value.Value()); |
870 | } |
871 | |
872 | static Namespace* CObjectToNamespacePointer(CObject* cobject) { |
873 | CObjectIntptr value(cobject); |
874 | return reinterpret_cast<Namespace*>(value.Value()); |
875 | } |
876 | |
877 | CObject* File::ExistsRequest(const CObjectArray& request) { |
878 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
879 | return CObject::IllegalArgumentError(); |
880 | } |
881 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
882 | RefCntReleaseScope<Namespace> rs(namespc); |
883 | if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
884 | return CObject::IllegalArgumentError(); |
885 | } |
886 | CObjectUint8Array filename(request[1]); |
887 | return CObject::Bool( |
888 | File::Exists(namespc, reinterpret_cast<const char*>(filename.Buffer()))); |
889 | } |
890 | |
891 | CObject* File::CreateRequest(const CObjectArray& request) { |
892 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
893 | return CObject::IllegalArgumentError(); |
894 | } |
895 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
896 | RefCntReleaseScope<Namespace> rs(namespc); |
897 | if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
898 | return CObject::IllegalArgumentError(); |
899 | } |
900 | CObjectUint8Array filename(request[1]); |
901 | return File::Create(namespc, reinterpret_cast<const char*>(filename.Buffer())) |
902 | ? CObject::True() |
903 | : CObject::NewOSError(); |
904 | } |
905 | |
906 | CObject* File::OpenRequest(const CObjectArray& request) { |
907 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
908 | return CObject::IllegalArgumentError(); |
909 | } |
910 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
911 | RefCntReleaseScope<Namespace> rs(namespc); |
912 | if ((request.Length() != 3) || !request[1]->IsUint8Array() || |
913 | !request[2]->IsInt32()) { |
914 | return CObject::IllegalArgumentError(); |
915 | } |
916 | CObjectUint8Array filename(request[1]); |
917 | CObjectInt32 mode(request[2]); |
918 | File::DartFileOpenMode dart_file_mode = |
919 | static_cast<File::DartFileOpenMode>(mode.Value()); |
920 | File::FileOpenMode file_mode = File::DartModeToFileMode(dart_file_mode); |
921 | File* file = File::Open( |
922 | namespc, reinterpret_cast<const char*>(filename.Buffer()), file_mode); |
923 | if (file == NULL) { |
924 | return CObject::NewOSError(); |
925 | } |
926 | return new CObjectIntptr( |
927 | CObject::NewIntptr(reinterpret_cast<intptr_t>(file))); |
928 | } |
929 | |
930 | CObject* File::DeleteRequest(const CObjectArray& request) { |
931 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
932 | return CObject::False(); |
933 | } |
934 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
935 | RefCntReleaseScope<Namespace> rs(namespc); |
936 | if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
937 | return CObject::False(); |
938 | } |
939 | CObjectUint8Array filename(request[1]); |
940 | return File::Delete(namespc, reinterpret_cast<const char*>(filename.Buffer())) |
941 | ? CObject::True() |
942 | : CObject::NewOSError(); |
943 | } |
944 | |
945 | CObject* File::RenameRequest(const CObjectArray& request) { |
946 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
947 | return CObject::IllegalArgumentError(); |
948 | } |
949 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
950 | RefCntReleaseScope<Namespace> rs(namespc); |
951 | if ((request.Length() != 3) || !request[1]->IsUint8Array() || |
952 | !request[2]->IsString()) { |
953 | return CObject::IllegalArgumentError(); |
954 | } |
955 | CObjectUint8Array old_path(request[1]); |
956 | CObjectString new_path(request[2]); |
957 | return File::Rename(namespc, reinterpret_cast<const char*>(old_path.Buffer()), |
958 | new_path.CString()) |
959 | ? CObject::True() |
960 | : CObject::NewOSError(); |
961 | } |
962 | |
963 | CObject* File::CopyRequest(const CObjectArray& request) { |
964 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
965 | return CObject::IllegalArgumentError(); |
966 | } |
967 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
968 | RefCntReleaseScope<Namespace> rs(namespc); |
969 | if ((request.Length() != 3) || !request[1]->IsUint8Array() || |
970 | !request[2]->IsString()) { |
971 | return CObject::IllegalArgumentError(); |
972 | } |
973 | CObjectUint8Array old_path(request[1]); |
974 | CObjectString new_path(request[2]); |
975 | return File::Copy(namespc, reinterpret_cast<const char*>(old_path.Buffer()), |
976 | new_path.CString()) |
977 | ? CObject::True() |
978 | : CObject::NewOSError(); |
979 | } |
980 | |
981 | CObject* File::ResolveSymbolicLinksRequest(const CObjectArray& request) { |
982 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
983 | return CObject::IllegalArgumentError(); |
984 | } |
985 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
986 | RefCntReleaseScope<Namespace> rs(namespc); |
987 | if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
988 | return CObject::IllegalArgumentError(); |
989 | } |
990 | CObjectUint8Array filename(request[1]); |
991 | const char* result = File::GetCanonicalPath( |
992 | namespc, reinterpret_cast<const char*>(filename.Buffer())); |
993 | if (result == NULL) { |
994 | return CObject::NewOSError(); |
995 | } |
996 | return new CObjectString(CObject::NewString(result)); |
997 | } |
998 | |
999 | CObject* File::CloseRequest(const CObjectArray& request) { |
1000 | if ((request.Length() != 1) || !request[0]->IsIntptr()) { |
1001 | return new CObjectIntptr(CObject::NewIntptr(-1)); |
1002 | } |
1003 | File* file = CObjectToFilePointer(request[0]); |
1004 | RefCntReleaseScope<File> rs(file); |
1005 | // We have retained a reference to the file here. Therefore the file's |
1006 | // destructor can't be running. Since no further requests are dispatched by |
1007 | // the Dart code after an async close call, this Close() can't be racing |
1008 | // with any other call on the file. We don't do an extra Release(), and we |
1009 | // don't delete the weak persistent handle. The file is closed here, but the |
1010 | // memory will be cleaned up when the finalizer runs. |
1011 | ASSERT(!file->IsClosed()); |
1012 | file->Close(); |
1013 | return new CObjectIntptr(CObject::NewIntptr(0)); |
1014 | } |
1015 | |
1016 | CObject* File::PositionRequest(const CObjectArray& request) { |
1017 | if ((request.Length() != 1) || !request[0]->IsIntptr()) { |
1018 | return CObject::IllegalArgumentError(); |
1019 | } |
1020 | File* file = CObjectToFilePointer(request[0]); |
1021 | RefCntReleaseScope<File> rs(file); |
1022 | if (file->IsClosed()) { |
1023 | return CObject::FileClosedError(); |
1024 | } |
1025 | const intptr_t return_value = file->Position(); |
1026 | if (return_value < 0) { |
1027 | return CObject::NewOSError(); |
1028 | } |
1029 | return new CObjectIntptr(CObject::NewIntptr(return_value)); |
1030 | } |
1031 | |
1032 | CObject* File::SetPositionRequest(const CObjectArray& request) { |
1033 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1034 | return CObject::IllegalArgumentError(); |
1035 | } |
1036 | File* file = CObjectToFilePointer(request[0]); |
1037 | RefCntReleaseScope<File> rs(file); |
1038 | if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) { |
1039 | return CObject::IllegalArgumentError(); |
1040 | } |
1041 | if (file->IsClosed()) { |
1042 | return CObject::FileClosedError(); |
1043 | } |
1044 | const int64_t position = CObjectInt32OrInt64ToInt64(request[1]); |
1045 | return file->SetPosition(position) ? CObject::True() : CObject::NewOSError(); |
1046 | } |
1047 | |
1048 | CObject* File::TruncateRequest(const CObjectArray& request) { |
1049 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1050 | return CObject::IllegalArgumentError(); |
1051 | } |
1052 | File* file = CObjectToFilePointer(request[0]); |
1053 | RefCntReleaseScope<File> rs(file); |
1054 | if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) { |
1055 | return CObject::IllegalArgumentError(); |
1056 | } |
1057 | if (file->IsClosed()) { |
1058 | return CObject::FileClosedError(); |
1059 | } |
1060 | const int64_t length = CObjectInt32OrInt64ToInt64(request[1]); |
1061 | if (file->Truncate(length)) { |
1062 | return CObject::True(); |
1063 | } |
1064 | return CObject::NewOSError(); |
1065 | } |
1066 | |
1067 | CObject* File::LengthRequest(const CObjectArray& request) { |
1068 | if ((request.Length() != 1) || !request[0]->IsIntptr()) { |
1069 | return CObject::IllegalArgumentError(); |
1070 | } |
1071 | File* file = CObjectToFilePointer(request[0]); |
1072 | RefCntReleaseScope<File> rs(file); |
1073 | if (file->IsClosed()) { |
1074 | return CObject::FileClosedError(); |
1075 | } |
1076 | const int64_t return_value = file->Length(); |
1077 | if (return_value < 0) { |
1078 | return CObject::NewOSError(); |
1079 | } |
1080 | return new CObjectInt64(CObject::NewInt64(return_value)); |
1081 | } |
1082 | |
1083 | CObject* File::LengthFromPathRequest(const CObjectArray& request) { |
1084 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1085 | return CObject::IllegalArgumentError(); |
1086 | } |
1087 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
1088 | RefCntReleaseScope<Namespace> rs(namespc); |
1089 | if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
1090 | return CObject::IllegalArgumentError(); |
1091 | } |
1092 | CObjectUint8Array filepath(request[1]); |
1093 | const int64_t return_value = File::LengthFromPath( |
1094 | namespc, reinterpret_cast<const char*>(filepath.Buffer())); |
1095 | if (return_value < 0) { |
1096 | return CObject::NewOSError(); |
1097 | } |
1098 | return new CObjectInt64(CObject::NewInt64(return_value)); |
1099 | } |
1100 | |
1101 | CObject* File::LastAccessedRequest(const CObjectArray& request) { |
1102 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1103 | return CObject::IllegalArgumentError(); |
1104 | } |
1105 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
1106 | RefCntReleaseScope<Namespace> rs(namespc); |
1107 | if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
1108 | return CObject::IllegalArgumentError(); |
1109 | } |
1110 | CObjectUint8Array filepath(request[1]); |
1111 | const int64_t return_value = File::LastAccessed( |
1112 | namespc, reinterpret_cast<const char*>(filepath.Buffer())); |
1113 | if (return_value < 0) { |
1114 | return CObject::NewOSError(); |
1115 | } |
1116 | return new CObjectIntptr( |
1117 | CObject::NewInt64(return_value * kMillisecondsPerSecond)); |
1118 | } |
1119 | |
1120 | CObject* File::SetLastAccessedRequest(const CObjectArray& request) { |
1121 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1122 | return CObject::IllegalArgumentError(); |
1123 | } |
1124 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
1125 | RefCntReleaseScope<Namespace> rs(namespc); |
1126 | if ((request.Length() != 3) || !request[1]->IsUint8Array() || |
1127 | !request[2]->IsInt32OrInt64()) { |
1128 | return CObject::IllegalArgumentError(); |
1129 | } |
1130 | CObjectUint8Array filepath(request[1]); |
1131 | const int64_t millis = CObjectInt32OrInt64ToInt64(request[2]); |
1132 | return File::SetLastAccessed( |
1133 | namespc, reinterpret_cast<const char*>(filepath.Buffer()), millis) |
1134 | ? CObject::Null() |
1135 | : CObject::NewOSError(); |
1136 | } |
1137 | |
1138 | CObject* File::LastModifiedRequest(const CObjectArray& request) { |
1139 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1140 | return CObject::IllegalArgumentError(); |
1141 | } |
1142 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
1143 | RefCntReleaseScope<Namespace> rs(namespc); |
1144 | if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
1145 | return CObject::IllegalArgumentError(); |
1146 | } |
1147 | CObjectUint8Array filepath(request[1]); |
1148 | const int64_t return_value = File::LastModified( |
1149 | namespc, reinterpret_cast<const char*>(filepath.Buffer())); |
1150 | if (return_value < 0) { |
1151 | return CObject::NewOSError(); |
1152 | } |
1153 | return new CObjectIntptr( |
1154 | CObject::NewInt64(return_value * kMillisecondsPerSecond)); |
1155 | } |
1156 | |
1157 | CObject* File::SetLastModifiedRequest(const CObjectArray& request) { |
1158 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1159 | return CObject::IllegalArgumentError(); |
1160 | } |
1161 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
1162 | RefCntReleaseScope<Namespace> rs(namespc); |
1163 | if ((request.Length() != 3) || !request[1]->IsUint8Array() || |
1164 | !request[2]->IsInt32OrInt64()) { |
1165 | return CObject::IllegalArgumentError(); |
1166 | } |
1167 | CObjectUint8Array filepath(request[1]); |
1168 | const int64_t millis = CObjectInt32OrInt64ToInt64(request[2]); |
1169 | return File::SetLastModified( |
1170 | namespc, reinterpret_cast<const char*>(filepath.Buffer()), millis) |
1171 | ? CObject::Null() |
1172 | : CObject::NewOSError(); |
1173 | } |
1174 | |
1175 | CObject* File::FlushRequest(const CObjectArray& request) { |
1176 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1177 | return CObject::IllegalArgumentError(); |
1178 | } |
1179 | File* file = CObjectToFilePointer(request[0]); |
1180 | RefCntReleaseScope<File> rs(file); |
1181 | if (file->IsClosed()) { |
1182 | return CObject::FileClosedError(); |
1183 | } |
1184 | return file->Flush() ? CObject::True() : CObject::NewOSError(); |
1185 | } |
1186 | |
1187 | CObject* File::ReadByteRequest(const CObjectArray& request) { |
1188 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1189 | return CObject::IllegalArgumentError(); |
1190 | } |
1191 | File* file = CObjectToFilePointer(request[0]); |
1192 | RefCntReleaseScope<File> rs(file); |
1193 | if (file->IsClosed()) { |
1194 | return CObject::FileClosedError(); |
1195 | } |
1196 | uint8_t buffer; |
1197 | const int64_t bytes_read = file->Read(reinterpret_cast<void*>(&buffer), 1); |
1198 | if (bytes_read < 0) { |
1199 | return CObject::NewOSError(); |
1200 | } |
1201 | if (bytes_read == 0) { |
1202 | return new CObjectIntptr(CObject::NewIntptr(-1)); |
1203 | } |
1204 | return new CObjectIntptr(CObject::NewIntptr(buffer)); |
1205 | } |
1206 | |
1207 | CObject* File::WriteByteRequest(const CObjectArray& request) { |
1208 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1209 | return CObject::IllegalArgumentError(); |
1210 | } |
1211 | File* file = CObjectToFilePointer(request[0]); |
1212 | RefCntReleaseScope<File> rs(file); |
1213 | if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) { |
1214 | return CObject::IllegalArgumentError(); |
1215 | } |
1216 | if (file->IsClosed()) { |
1217 | return CObject::FileClosedError(); |
1218 | } |
1219 | const int64_t byte = CObjectInt32OrInt64ToInt64(request[1]); |
1220 | uint8_t buffer = static_cast<uint8_t>(byte & 0xff); |
1221 | return file->WriteFully(reinterpret_cast<void*>(&buffer), 1) |
1222 | ? new CObjectInt64(CObject::NewInt64(1)) |
1223 | : CObject::NewOSError(); |
1224 | } |
1225 | |
1226 | CObject* File::ReadRequest(const CObjectArray& request) { |
1227 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1228 | return CObject::IllegalArgumentError(); |
1229 | } |
1230 | File* file = CObjectToFilePointer(request[0]); |
1231 | RefCntReleaseScope<File> rs(file); |
1232 | if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) { |
1233 | return CObject::IllegalArgumentError(); |
1234 | } |
1235 | if (file->IsClosed()) { |
1236 | return CObject::FileClosedError(); |
1237 | } |
1238 | const int64_t length = CObjectInt32OrInt64ToInt64(request[1]); |
1239 | Dart_CObject* io_buffer = CObject::NewIOBuffer(length); |
1240 | if (io_buffer == NULL) { |
1241 | return CObject::NewOSError(); |
1242 | } |
1243 | uint8_t* data = io_buffer->value.as_external_typed_data.data; |
1244 | const int64_t bytes_read = file->Read(data, length); |
1245 | if (bytes_read < 0) { |
1246 | CObject::FreeIOBufferData(io_buffer); |
1247 | return CObject::NewOSError(); |
1248 | } |
1249 | CObjectExternalUint8Array* external_array = |
1250 | new CObjectExternalUint8Array(io_buffer); |
1251 | external_array->SetLength(bytes_read); |
1252 | CObjectArray* result = new CObjectArray(CObject::NewArray(2)); |
1253 | result->SetAt(0, new CObjectIntptr(CObject::NewInt32(0))); |
1254 | result->SetAt(1, external_array); |
1255 | return result; |
1256 | } |
1257 | |
1258 | CObject* File::ReadIntoRequest(const CObjectArray& request) { |
1259 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1260 | return CObject::IllegalArgumentError(); |
1261 | } |
1262 | File* file = CObjectToFilePointer(request[0]); |
1263 | RefCntReleaseScope<File> rs(file); |
1264 | if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) { |
1265 | return CObject::IllegalArgumentError(); |
1266 | } |
1267 | if (file->IsClosed()) { |
1268 | return CObject::FileClosedError(); |
1269 | } |
1270 | const int64_t length = CObjectInt32OrInt64ToInt64(request[1]); |
1271 | Dart_CObject* io_buffer = CObject::NewIOBuffer(length); |
1272 | if (io_buffer == NULL) { |
1273 | return CObject::NewOSError(); |
1274 | } |
1275 | uint8_t* data = io_buffer->value.as_external_typed_data.data; |
1276 | const int64_t bytes_read = file->Read(data, length); |
1277 | if (bytes_read < 0) { |
1278 | CObject::FreeIOBufferData(io_buffer); |
1279 | return CObject::NewOSError(); |
1280 | } |
1281 | CObjectExternalUint8Array* external_array = |
1282 | new CObjectExternalUint8Array(io_buffer); |
1283 | external_array->SetLength(bytes_read); |
1284 | CObjectArray* result = new CObjectArray(CObject::NewArray(3)); |
1285 | result->SetAt(0, new CObjectIntptr(CObject::NewInt32(0))); |
1286 | result->SetAt(1, new CObjectInt64(CObject::NewInt64(bytes_read))); |
1287 | result->SetAt(2, external_array); |
1288 | return result; |
1289 | } |
1290 | |
1291 | static int SizeInBytes(Dart_TypedData_Type type) { |
1292 | switch (type) { |
1293 | case Dart_TypedData_kInt8: |
1294 | case Dart_TypedData_kUint8: |
1295 | case Dart_TypedData_kUint8Clamped: |
1296 | return 1; |
1297 | case Dart_TypedData_kInt16: |
1298 | case Dart_TypedData_kUint16: |
1299 | return 2; |
1300 | case Dart_TypedData_kInt32: |
1301 | case Dart_TypedData_kUint32: |
1302 | case Dart_TypedData_kFloat32: |
1303 | return 4; |
1304 | case Dart_TypedData_kInt64: |
1305 | case Dart_TypedData_kUint64: |
1306 | case Dart_TypedData_kFloat64: |
1307 | return 8; |
1308 | case Dart_TypedData_kInt32x4: |
1309 | case Dart_TypedData_kFloat32x4: |
1310 | case Dart_TypedData_kFloat64x2: |
1311 | return 16; |
1312 | default: |
1313 | break; |
1314 | } |
1315 | UNREACHABLE(); |
1316 | return -1; |
1317 | } |
1318 | |
1319 | CObject* File::WriteFromRequest(const CObjectArray& request) { |
1320 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1321 | return CObject::IllegalArgumentError(); |
1322 | } |
1323 | File* file = CObjectToFilePointer(request[0]); |
1324 | RefCntReleaseScope<File> rs(file); |
1325 | if ((request.Length() != 4) || |
1326 | (!request[1]->IsTypedData() && !request[1]->IsArray()) || |
1327 | !request[2]->IsInt32OrInt64() || !request[3]->IsInt32OrInt64()) { |
1328 | return CObject::IllegalArgumentError(); |
1329 | } |
1330 | if (file->IsClosed()) { |
1331 | return CObject::FileClosedError(); |
1332 | } |
1333 | int64_t start = CObjectInt32OrInt64ToInt64(request[2]); |
1334 | int64_t end = CObjectInt32OrInt64ToInt64(request[3]); |
1335 | int64_t length = end - start; |
1336 | uint8_t* buffer_start; |
1337 | if (request[1]->IsTypedData()) { |
1338 | CObjectTypedData typed_data(request[1]); |
1339 | start = start * SizeInBytes(typed_data.Type()); |
1340 | length = length * SizeInBytes(typed_data.Type()); |
1341 | buffer_start = typed_data.Buffer() + start; |
1342 | } else { |
1343 | CObjectArray array(request[1]); |
1344 | buffer_start = Dart_ScopeAllocate(length); |
1345 | for (int i = 0; i < length; i++) { |
1346 | if (array[i + start]->IsInt32OrInt64()) { |
1347 | int64_t value = CObjectInt32OrInt64ToInt64(array[i + start]); |
1348 | buffer_start[i] = static_cast<uint8_t>(value & 0xFF); |
1349 | } else { |
1350 | // Unsupported type. |
1351 | return CObject::IllegalArgumentError(); |
1352 | } |
1353 | } |
1354 | start = 0; |
1355 | } |
1356 | return file->WriteFully(reinterpret_cast<void*>(buffer_start), length) |
1357 | ? new CObjectInt64(CObject::NewInt64(length)) |
1358 | : CObject::NewOSError(); |
1359 | } |
1360 | |
1361 | CObject* File::CreateLinkRequest(const CObjectArray& request) { |
1362 | if ((request.Length() != 3) || !request[0]->IsIntptr()) { |
1363 | return CObject::IllegalArgumentError(); |
1364 | } |
1365 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
1366 | RefCntReleaseScope<Namespace> rs(namespc); |
1367 | if (!request[1]->IsUint8Array() || !request[2]->IsString()) { |
1368 | return CObject::IllegalArgumentError(); |
1369 | } |
1370 | CObjectUint8Array link_name(request[1]); |
1371 | CObjectString target_name(request[2]); |
1372 | return File::CreateLink(namespc, |
1373 | reinterpret_cast<const char*>(link_name.Buffer()), |
1374 | target_name.CString()) |
1375 | ? CObject::True() |
1376 | : CObject::NewOSError(); |
1377 | } |
1378 | |
1379 | CObject* File::DeleteLinkRequest(const CObjectArray& request) { |
1380 | if ((request.Length() != 2) || !request[0]->IsIntptr()) { |
1381 | return CObject::IllegalArgumentError(); |
1382 | } |
1383 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
1384 | RefCntReleaseScope<Namespace> rs(namespc); |
1385 | if (!request[1]->IsUint8Array()) { |
1386 | return CObject::IllegalArgumentError(); |
1387 | } |
1388 | CObjectUint8Array link_path(request[1]); |
1389 | return File::DeleteLink(namespc, |
1390 | reinterpret_cast<const char*>(link_path.Buffer())) |
1391 | ? CObject::True() |
1392 | : CObject::NewOSError(); |
1393 | } |
1394 | |
1395 | CObject* File::RenameLinkRequest(const CObjectArray& request) { |
1396 | if ((request.Length() != 3) || !request[0]->IsIntptr()) { |
1397 | return CObject::IllegalArgumentError(); |
1398 | } |
1399 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
1400 | RefCntReleaseScope<Namespace> rs(namespc); |
1401 | if (!request[1]->IsUint8Array() || !request[2]->IsString()) { |
1402 | return CObject::IllegalArgumentError(); |
1403 | } |
1404 | CObjectUint8Array old_path(request[1]); |
1405 | CObjectString new_path(request[2]); |
1406 | return File::RenameLink(namespc, |
1407 | reinterpret_cast<const char*>(old_path.Buffer()), |
1408 | new_path.CString()) |
1409 | ? CObject::True() |
1410 | : CObject::NewOSError(); |
1411 | } |
1412 | |
1413 | CObject* File::LinkTargetRequest(const CObjectArray& request) { |
1414 | if ((request.Length() != 2) || !request[0]->IsIntptr()) { |
1415 | return CObject::IllegalArgumentError(); |
1416 | } |
1417 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
1418 | RefCntReleaseScope<Namespace> rs(namespc); |
1419 | if (!request[1]->IsUint8Array()) { |
1420 | return CObject::IllegalArgumentError(); |
1421 | } |
1422 | CObjectUint8Array link_path(request[1]); |
1423 | const char* target = File::LinkTarget( |
1424 | namespc, reinterpret_cast<const char*>(link_path.Buffer())); |
1425 | if (target == NULL) { |
1426 | return CObject::NewOSError(); |
1427 | } |
1428 | return new CObjectString(CObject::NewString(target)); |
1429 | } |
1430 | |
1431 | CObject* File::TypeRequest(const CObjectArray& request) { |
1432 | if ((request.Length() != 3) || !request[0]->IsIntptr()) { |
1433 | return CObject::IllegalArgumentError(); |
1434 | } |
1435 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
1436 | RefCntReleaseScope<Namespace> rs(namespc); |
1437 | if (!request[1]->IsUint8Array() || !request[2]->IsBool()) { |
1438 | return CObject::IllegalArgumentError(); |
1439 | } |
1440 | CObjectUint8Array path(request[1]); |
1441 | CObjectBool follow_links(request[2]); |
1442 | File::Type type = |
1443 | File::GetType(namespc, reinterpret_cast<const char*>(path.Buffer()), |
1444 | follow_links.Value()); |
1445 | return new CObjectInt32(CObject::NewInt32(type)); |
1446 | } |
1447 | |
1448 | CObject* File::IdenticalRequest(const CObjectArray& request) { |
1449 | if ((request.Length() != 3) || !request[0]->IsIntptr()) { |
1450 | return CObject::IllegalArgumentError(); |
1451 | } |
1452 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
1453 | RefCntReleaseScope<Namespace> rs(namespc); |
1454 | if (!request[1]->IsString() || !request[2]->IsString()) { |
1455 | return CObject::IllegalArgumentError(); |
1456 | } |
1457 | CObjectString path1(request[1]); |
1458 | CObjectString path2(request[2]); |
1459 | File::Identical result = |
1460 | File::AreIdentical(namespc, path1.CString(), namespc, path2.CString()); |
1461 | if (result == File::kError) { |
1462 | return CObject::NewOSError(); |
1463 | } |
1464 | return (result == File::kIdentical) ? CObject::True() : CObject::False(); |
1465 | } |
1466 | |
1467 | CObject* File::StatRequest(const CObjectArray& request) { |
1468 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1469 | return CObject::IllegalArgumentError(); |
1470 | } |
1471 | Namespace* namespc = CObjectToNamespacePointer(request[0]); |
1472 | RefCntReleaseScope<Namespace> rs(namespc); |
1473 | if ((request.Length() != 2) || !request[1]->IsString()) { |
1474 | return CObject::IllegalArgumentError(); |
1475 | } |
1476 | int64_t data[File::kStatSize]; |
1477 | CObjectString path(request[1]); |
1478 | File::Stat(namespc, path.CString(), data); |
1479 | if (data[File::kType] == File::kDoesNotExist) { |
1480 | return CObject::NewOSError(); |
1481 | } |
1482 | CObjectArray* result = new CObjectArray(CObject::NewArray(File::kStatSize)); |
1483 | for (int i = 0; i < File::kStatSize; ++i) { |
1484 | result->SetAt(i, new CObjectInt64(CObject::NewInt64(data[i]))); |
1485 | } |
1486 | CObjectArray* wrapper = new CObjectArray(CObject::NewArray(2)); |
1487 | wrapper->SetAt(0, new CObjectInt32(CObject::NewInt32(CObject::kSuccess))); |
1488 | wrapper->SetAt(1, result); |
1489 | return wrapper; |
1490 | } |
1491 | |
1492 | CObject* File::LockRequest(const CObjectArray& request) { |
1493 | if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
1494 | return CObject::IllegalArgumentError(); |
1495 | } |
1496 | File* file = CObjectToFilePointer(request[0]); |
1497 | RefCntReleaseScope<File> rs(file); |
1498 | if ((request.Length() != 4) || !request[1]->IsInt32OrInt64() || |
1499 | !request[2]->IsInt32OrInt64() || !request[3]->IsInt32OrInt64()) { |
1500 | return CObject::IllegalArgumentError(); |
1501 | } |
1502 | if (file->IsClosed()) { |
1503 | return CObject::FileClosedError(); |
1504 | } |
1505 | const int64_t lock = CObjectInt32OrInt64ToInt64(request[1]); |
1506 | const int64_t start = CObjectInt32OrInt64ToInt64(request[2]); |
1507 | const int64_t end = CObjectInt32OrInt64ToInt64(request[3]); |
1508 | return file->Lock(static_cast<File::LockType>(lock), start, end) |
1509 | ? CObject::True() |
1510 | : CObject::NewOSError(); |
1511 | } |
1512 | |
1513 | // Inspired by sdk/lib/core/uri.dart |
1514 | UriDecoder::UriDecoder(const char* uri) : uri_(uri) { |
1515 | const char* ch = uri; |
1516 | while ((*ch != '\0') && (*ch != '%')) { |
1517 | ch++; |
1518 | } |
1519 | if (*ch == 0) { |
1520 | // if there are no '%', nothing to decode, refer to original as decoded. |
1521 | decoded_ = const_cast<char*>(uri); |
1522 | return; |
1523 | } |
1524 | const intptr_t len = strlen(uri); |
1525 | // Decoded string should be shorter than original because of |
1526 | // percent-encoding. |
1527 | char* dest = reinterpret_cast<char*>(malloc(len + 1)); |
1528 | int i = ch - uri; |
1529 | // Copy all characters up to first '%' at index i. |
1530 | strncpy(dest, uri, i); |
1531 | decoded_ = dest; |
1532 | dest += i; |
1533 | while (*ch != '\0') { |
1534 | if (*ch != '%') { |
1535 | *(dest++) = *(ch++); |
1536 | continue; |
1537 | } |
1538 | if ((i + 3 > len) || !HexCharPairToByte(ch + 1, dest)) { |
1539 | free(decoded_); |
1540 | decoded_ = NULL; |
1541 | return; |
1542 | } |
1543 | ++dest; |
1544 | ch += 3; |
1545 | } |
1546 | *dest = 0; |
1547 | } |
1548 | |
1549 | UriDecoder::~UriDecoder() { |
1550 | if (uri_ != decoded_ && decoded_ != NULL) { |
1551 | free(decoded_); |
1552 | } |
1553 | } |
1554 | |
1555 | bool UriDecoder::HexCharPairToByte(const char* pch, char* const dest) { |
1556 | int byte = 0; |
1557 | for (int i = 0; i < 2; i++) { |
1558 | char char_code = *(pch + i); |
1559 | if (0x30 <= char_code && char_code <= 0x39) { |
1560 | byte = byte * 16 + char_code - 0x30; |
1561 | } else { |
1562 | // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66). |
1563 | char_code |= 0x20; |
1564 | if (0x61 <= char_code && char_code <= 0x66) { |
1565 | byte = byte * 16 + char_code - 0x57; |
1566 | } else { |
1567 | return false; |
1568 | } |
1569 | } |
1570 | } |
1571 | *dest = byte; |
1572 | return true; |
1573 | } |
1574 | |
1575 | } // namespace bin |
1576 | } // namespace dart |
1577 | |