mt-lang 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -0
  3. data/Rakefile +1 -1
  4. data/docs/index.html +13 -3
  5. data/lib/milk_tea/base.rb +7 -2
  6. data/std/asset_pack.mt +267 -0
  7. data/std/async/libuv_runtime.mt +539 -0
  8. data/std/async/mailbox.mt +191 -0
  9. data/std/async/runtime.mt +87 -0
  10. data/std/async.mt +87 -0
  11. data/std/base64.mt +185 -0
  12. data/std/behavior_tree.mt +396 -0
  13. data/std/binary.mt +314 -0
  14. data/std/binary_heap.mt +157 -0
  15. data/std/bitset.mt +239 -0
  16. data/std/box2d.mt +535 -0
  17. data/std/bytes.mt +46 -0
  18. data/std/c/box2d.mt +1254 -0
  19. data/std/c/cgltf.mt +647 -0
  20. data/std/c/cjson.mt +106 -0
  21. data/std/c/crypto.mt +11 -0
  22. data/std/c/crypto_support.h +51 -0
  23. data/std/c/ctype.mt +19 -0
  24. data/std/c/ctype_bindgen.h +29 -0
  25. data/std/c/curl.mt +1223 -0
  26. data/std/c/enet.mt +473 -0
  27. data/std/c/errno.mt +41 -0
  28. data/std/c/errno_bindgen.h +53 -0
  29. data/std/c/flecs.mt +2945 -0
  30. data/std/c/fs.linux.mt +43 -0
  31. data/std/c/fs.windows.mt +43 -0
  32. data/std/c/fs_support.h +954 -0
  33. data/std/c/gl.mt +2065 -0
  34. data/std/c/gl_registry_helpers.h +10748 -0
  35. data/std/c/glfw.mt +499 -0
  36. data/std/c/libc.mt +167 -0
  37. data/std/c/libuv.mt +1491 -0
  38. data/std/c/math.mt +22 -0
  39. data/std/c/math_bindgen.h +31 -0
  40. data/std/c/miniaudio.mt +3420 -0
  41. data/std/c/pcre2.mt +735 -0
  42. data/std/c/process.mt +58 -0
  43. data/std/c/process_support.h +1147 -0
  44. data/std/c/raygui.mt +1602 -0
  45. data/std/c/raylib.mt +1245 -0
  46. data/std/c/raymath.mt +163 -0
  47. data/std/c/rlgl.mt +434 -0
  48. data/std/c/rpng.mt +60 -0
  49. data/std/c/rres.mt +187 -0
  50. data/std/c/sdl3.mt +4132 -0
  51. data/std/c/sqlite3.mt +951 -0
  52. data/std/c/stb_image.mt +52 -0
  53. data/std/c/stb_image_resize2.mt +128 -0
  54. data/std/c/stb_image_write.mt +19 -0
  55. data/std/c/stb_rect_pack.mt +38 -0
  56. data/std/c/stb_truetype.mt +175 -0
  57. data/std/c/stb_vorbis.mt +31 -0
  58. data/std/c/stdio.mt +90 -0
  59. data/std/c/steamworks.h +5769 -0
  60. data/std/c/steamworks.mt +4771 -0
  61. data/std/c/string.mt +16 -0
  62. data/std/c/string_bindgen.h +36 -0
  63. data/std/c/sync.mt +33 -0
  64. data/std/c/sync_support.h +219 -0
  65. data/std/c/terminal.mt +24 -0
  66. data/std/c/terminal_support.h +408 -0
  67. data/std/c/time.mt +26 -0
  68. data/std/c/tls.mt +24 -0
  69. data/std/c/tls_support.h +626 -0
  70. data/std/c/tracy.mt +53 -0
  71. data/std/c/zlib.mt +20 -0
  72. data/std/c/zlib_support.h +239 -0
  73. data/std/c/zstd.mt +231 -0
  74. data/std/cell.mt +60 -0
  75. data/std/cgltf.mt +115 -0
  76. data/std/cjson.mt +90 -0
  77. data/std/cli.mt +768 -0
  78. data/std/cookie.mt +198 -0
  79. data/std/counter.mt +160 -0
  80. data/std/crypto.mt +74 -0
  81. data/std/cstring.mt +13 -0
  82. data/std/ctype.mt +52 -0
  83. data/std/curl/runtime.mt +261 -0
  84. data/std/curl.mt +38 -0
  85. data/std/deque.mt +348 -0
  86. data/std/encoding.mt +93 -0
  87. data/std/enet.mt +133 -0
  88. data/std/env.mt +41 -0
  89. data/std/errno.mt +47 -0
  90. data/std/flecs.mt +978 -0
  91. data/std/fmt.mt +271 -0
  92. data/std/fs.linux.mt +696 -0
  93. data/std/fs.windows.mt +711 -0
  94. data/std/fsm.mt +267 -0
  95. data/std/gl.mt +2062 -0
  96. data/std/glfw.mt +471 -0
  97. data/std/goap.mt +342 -0
  98. data/std/graph.mt +521 -0
  99. data/std/gzip.mt +77 -0
  100. data/std/hash.mt +429 -0
  101. data/std/http/server.mt +810 -0
  102. data/std/http.mt +1227 -0
  103. data/std/intern.mt +53 -0
  104. data/std/jobs.mt +288 -0
  105. data/std/json.mt +627 -0
  106. data/std/libc.mt +34 -0
  107. data/std/libuv.mt +554 -0
  108. data/std/linear_algebra.mt +243 -0
  109. data/std/linked_map.mt +372 -0
  110. data/std/linked_map_view.mt +49 -0
  111. data/std/linked_set.mt +117 -0
  112. data/std/log.mt +69 -0
  113. data/std/map.mt +418 -0
  114. data/std/math.mt +24 -0
  115. data/std/mem/arena.mt +137 -0
  116. data/std/mem/endian.mt +51 -0
  117. data/std/mem/heap.mt +298 -0
  118. data/std/mem/pool.mt +164 -0
  119. data/std/mem/stack.mt +47 -0
  120. data/std/mem/tracking.mt +119 -0
  121. data/std/miniaudio.mt +1299 -0
  122. data/std/multiset.mt +191 -0
  123. data/std/net/channel.mt +727 -0
  124. data/std/net/clock.mt +237 -0
  125. data/std/net/discovery.mt +277 -0
  126. data/std/net/lobby.mt +813 -0
  127. data/std/net/manager.mt +539 -0
  128. data/std/net/mux.mt +839 -0
  129. data/std/net/nat.mt +122 -0
  130. data/std/net/packet.mt +219 -0
  131. data/std/net/punch.mt +162 -0
  132. data/std/net/rpc.mt +119 -0
  133. data/std/net/session.mt +1157 -0
  134. data/std/net/stun.mt +252 -0
  135. data/std/net/sync.mt +183 -0
  136. data/std/net/turn.mt +459 -0
  137. data/std/net.mt +2944 -0
  138. data/std/oauth2.mt +408 -0
  139. data/std/option.mt +51 -0
  140. data/std/ordered_map.mt +497 -0
  141. data/std/ordered_set.mt +348 -0
  142. data/std/path.mt +368 -0
  143. data/std/pcre2/runtime.mt +35 -0
  144. data/std/pcre2.mt +78 -0
  145. data/std/priority_queue.mt +55 -0
  146. data/std/process.mt +837 -0
  147. data/std/queue.mt +55 -0
  148. data/std/random.mt +146 -0
  149. data/std/raygui.mt +99 -0
  150. data/std/raylib/debug_console.mt +71 -0
  151. data/std/raylib/easing.mt +388 -0
  152. data/std/raylib/packed_assets.mt +255 -0
  153. data/std/raylib/runtime.mt +30 -0
  154. data/std/raylib/tracy_gpu.mt +37 -0
  155. data/std/raylib.mt +1510 -0
  156. data/std/raymath.mt +153 -0
  157. data/std/result.mt +89 -0
  158. data/std/rlgl.mt +268 -0
  159. data/std/rpng.mt +46 -0
  160. data/std/rres.mt +36 -0
  161. data/std/sdl3/runtime.mt +54 -0
  162. data/std/sdl3.mt +1731 -0
  163. data/std/serialize.mt +68 -0
  164. data/std/set.mt +124 -0
  165. data/std/spatial.mt +176 -0
  166. data/std/sqlite3.mt +151 -0
  167. data/std/stack.mt +55 -0
  168. data/std/stb_image.mt +47 -0
  169. data/std/stb_image_resize2.mt +41 -0
  170. data/std/stb_image_write.mt +17 -0
  171. data/std/stb_rect_pack.mt +15 -0
  172. data/std/stb_truetype.mt +77 -0
  173. data/std/stb_vorbis.mt +16 -0
  174. data/std/stdio.mt +88 -0
  175. data/std/steamworks.mt +1542 -0
  176. data/std/str.mt +293 -0
  177. data/std/string.mt +234 -0
  178. data/std/sync.mt +194 -0
  179. data/std/tar.mt +704 -0
  180. data/std/terminal.mt +1002 -0
  181. data/std/testing.mt +266 -0
  182. data/std/thread.mt +120 -0
  183. data/std/time.mt +105 -0
  184. data/std/tls.mt +616 -0
  185. data/std/toml.mt +1310 -0
  186. data/std/tracy.mt +42 -0
  187. data/std/uri.mt +118 -0
  188. data/std/url.mt +372 -0
  189. data/std/vec.mt +433 -0
  190. data/std/zstd.mt +94 -0
  191. metadata +187 -2
@@ -0,0 +1,954 @@
1
+ #ifndef _GNU_SOURCE
2
+ #define _GNU_SOURCE
3
+ #endif
4
+
5
+ #ifndef _POSIX_C_SOURCE
6
+ #define _POSIX_C_SOURCE 200809L
7
+ #endif
8
+
9
+ #ifndef MT_FS_SUPPORT_H
10
+ #define MT_FS_SUPPORT_H
11
+
12
+ #include <errno.h>
13
+ #include <fcntl.h>
14
+ #include <stdbool.h>
15
+ #include <stdint.h>
16
+ #include <stdlib.h>
17
+ #include <string.h>
18
+ #include <sys/stat.h>
19
+ #include <sys/types.h>
20
+ #include <uv.h>
21
+
22
+ typedef struct mt_fs_string {
23
+ char* data;
24
+ uintptr_t len;
25
+ } mt_fs_string;
26
+
27
+ typedef struct mt_fs_entries {
28
+ char** data;
29
+ uintptr_t* lengths;
30
+ uintptr_t count;
31
+ } mt_fs_entries;
32
+
33
+ typedef struct mt_fs_error {
34
+ int code;
35
+ char* message_data;
36
+ uintptr_t message_len;
37
+ } mt_fs_error;
38
+
39
+ typedef struct mt_fs_metadata {
40
+ int kind;
41
+ int mode;
42
+ uintptr_t size;
43
+ intptr_t modified_seconds;
44
+ intptr_t modified_nanoseconds;
45
+ } mt_fs_metadata;
46
+
47
+ enum {
48
+ MT_FS_KIND_NONE = 0,
49
+ MT_FS_KIND_FILE = 1,
50
+ MT_FS_KIND_DIRECTORY = 2,
51
+ MT_FS_KIND_OTHER = 3,
52
+ };
53
+
54
+ static inline void mt_fs_reset_string(mt_fs_string* value) {
55
+ if (value == NULL) {
56
+ return;
57
+ }
58
+
59
+ value->data = NULL;
60
+ value->len = 0;
61
+ }
62
+
63
+ static inline void mt_fs_reset_entries(mt_fs_entries* value) {
64
+ if (value == NULL) {
65
+ return;
66
+ }
67
+
68
+ value->data = NULL;
69
+ value->lengths = NULL;
70
+ value->count = 0;
71
+ }
72
+
73
+ static inline void mt_fs_reset_error(mt_fs_error* error) {
74
+ if (error == NULL) {
75
+ return;
76
+ }
77
+
78
+ error->code = 0;
79
+ error->message_data = NULL;
80
+ error->message_len = 0;
81
+ }
82
+
83
+ static inline void mt_fs_reset_metadata(mt_fs_metadata* value) {
84
+ if (value == NULL) {
85
+ return;
86
+ }
87
+
88
+ value->kind = MT_FS_KIND_NONE;
89
+ value->mode = 0;
90
+ value->size = 0;
91
+ value->modified_seconds = 0;
92
+ value->modified_nanoseconds = 0;
93
+ }
94
+
95
+ static inline int mt_fs_set_message(mt_fs_error* error, int code, const char* prefix, const char* detail) {
96
+ if (error == NULL) {
97
+ return code == 0 ? -1 : code;
98
+ }
99
+
100
+ size_t prefix_len = prefix == NULL ? 0 : strlen(prefix);
101
+ size_t detail_len = detail == NULL ? 0 : strlen(detail);
102
+ size_t separator_len = prefix_len != 0 && detail_len != 0 ? 2 : 0;
103
+ size_t total_len = prefix_len + separator_len + detail_len;
104
+
105
+ char* message = NULL;
106
+ if (total_len != 0) {
107
+ message = (char*) malloc(total_len);
108
+ if (message != NULL) {
109
+ size_t offset = 0;
110
+ if (prefix_len != 0) {
111
+ memcpy(message + offset, prefix, prefix_len);
112
+ offset += prefix_len;
113
+ }
114
+ if (separator_len != 0) {
115
+ memcpy(message + offset, ": ", separator_len);
116
+ offset += separator_len;
117
+ }
118
+ if (detail_len != 0) {
119
+ memcpy(message + offset, detail, detail_len);
120
+ }
121
+ }
122
+ }
123
+
124
+ error->code = code;
125
+ error->message_data = message;
126
+ error->message_len = (uintptr_t) total_len;
127
+ return code == 0 ? -1 : code;
128
+ }
129
+
130
+ static inline int mt_fs_set_errno_error(mt_fs_error* error, int code, const char* prefix) {
131
+ return mt_fs_set_message(error, code, prefix, strerror(code));
132
+ }
133
+
134
+ static inline int mt_fs_set_uv_error(mt_fs_error* error, uv_fs_t* req, int uv_code, const char* prefix) {
135
+ int code = req == NULL ? 0 : uv_fs_get_system_error(req);
136
+ if (code < 0) {
137
+ code = -code;
138
+ }
139
+ if (code == 0) {
140
+ code = uv_code < 0 ? -uv_code : uv_code;
141
+ }
142
+ return mt_fs_set_message(error, code, prefix, uv_strerror(uv_code));
143
+ }
144
+
145
+ static inline bool mt_fs_is_separator(char value) {
146
+ return value == '/' || value == '\\';
147
+ }
148
+
149
+ static inline int mt_fs_root_length(const char* path) {
150
+ if (path == NULL || path[0] == '\0') {
151
+ return 0;
152
+ }
153
+
154
+ if (mt_fs_is_separator(path[0])) {
155
+ if (mt_fs_is_separator(path[1])) {
156
+ return 2;
157
+ }
158
+ return 1;
159
+ }
160
+
161
+ if (((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z')) && path[1] == ':') {
162
+ if (mt_fs_is_separator(path[2])) {
163
+ return 3;
164
+ }
165
+ return 2;
166
+ }
167
+
168
+ return 0;
169
+ }
170
+
171
+ static inline void mt_fs_release_entry_buffers(char** data, uintptr_t* lengths, uintptr_t count) {
172
+ if (data != NULL) {
173
+ for (uintptr_t index = 0; index < count; index += 1) {
174
+ free(data[index]);
175
+ }
176
+ }
177
+
178
+ free(data);
179
+ free(lengths);
180
+ }
181
+
182
+ static inline int mt_fs_close_file(uv_file descriptor, mt_fs_error* out_error, const char* prefix) {
183
+ uv_fs_t close_req;
184
+ int close_status = uv_fs_close(NULL, &close_req, descriptor, NULL);
185
+ if (close_status < 0) {
186
+ int result = mt_fs_set_uv_error(out_error, &close_req, close_status, prefix);
187
+ uv_fs_req_cleanup(&close_req);
188
+ return result;
189
+ }
190
+ uv_fs_req_cleanup(&close_req);
191
+ return 0;
192
+ }
193
+
194
+ static inline int mt_fs_path_kind(const char* path) {
195
+ if (path == NULL || path[0] == '\0') {
196
+ return MT_FS_KIND_NONE;
197
+ }
198
+
199
+ uv_fs_t req;
200
+ int status = uv_fs_stat(NULL, &req, path, NULL);
201
+ if (status < 0) {
202
+ uv_fs_req_cleanup(&req);
203
+ return MT_FS_KIND_NONE;
204
+ }
205
+
206
+ const uv_stat_t* info = uv_fs_get_statbuf(&req);
207
+ int kind = MT_FS_KIND_OTHER;
208
+ if (info != NULL) {
209
+ if (S_ISREG(info->st_mode)) {
210
+ kind = MT_FS_KIND_FILE;
211
+ } else if (S_ISDIR(info->st_mode)) {
212
+ kind = MT_FS_KIND_DIRECTORY;
213
+ }
214
+ }
215
+
216
+ uv_fs_req_cleanup(&req);
217
+ return kind;
218
+ }
219
+
220
+ static inline int mt_fs_get_metadata(const char* path, mt_fs_metadata* out_metadata, mt_fs_error* out_error) {
221
+ mt_fs_reset_metadata(out_metadata);
222
+ mt_fs_reset_error(out_error);
223
+
224
+ if (path == NULL || path[0] == '\0') {
225
+ return mt_fs_set_message(out_error, -1, "fs metadata failed", "path cannot be empty");
226
+ }
227
+
228
+ uv_fs_t req;
229
+ int status = uv_fs_lstat(NULL, &req, path, NULL);
230
+ if (status < 0) {
231
+ int result = mt_fs_set_uv_error(out_error, &req, status, "fs metadata failed");
232
+ uv_fs_req_cleanup(&req);
233
+ return result;
234
+ }
235
+
236
+ const uv_stat_t* info = uv_fs_get_statbuf(&req);
237
+ if (info == NULL) {
238
+ uv_fs_req_cleanup(&req);
239
+ return mt_fs_set_message(out_error, EIO, "fs metadata failed", "missing stat data");
240
+ }
241
+
242
+ out_metadata->mode = (int) (info->st_mode & 07777);
243
+ out_metadata->size = (uintptr_t) info->st_size;
244
+ out_metadata->modified_seconds = (intptr_t) info->st_mtim.tv_sec;
245
+ out_metadata->modified_nanoseconds = (intptr_t) info->st_mtim.tv_nsec;
246
+ if (S_ISREG(info->st_mode)) {
247
+ out_metadata->kind = MT_FS_KIND_FILE;
248
+ } else if (S_ISDIR(info->st_mode)) {
249
+ out_metadata->kind = MT_FS_KIND_DIRECTORY;
250
+ } else {
251
+ out_metadata->kind = MT_FS_KIND_OTHER;
252
+ }
253
+
254
+ uv_fs_req_cleanup(&req);
255
+ return 0;
256
+ }
257
+
258
+ static inline int mt_fs_read_text(const char* path, mt_fs_string* out_text, mt_fs_error* out_error) {
259
+ mt_fs_reset_string(out_text);
260
+ mt_fs_reset_error(out_error);
261
+
262
+ if (path == NULL || path[0] == '\0') {
263
+ return mt_fs_set_message(out_error, -1, "fs read failed", "path cannot be empty");
264
+ }
265
+
266
+ uv_fs_t open_req;
267
+ int open_status = uv_fs_open(NULL, &open_req, path, O_RDONLY, 0, NULL);
268
+ if (open_status < 0) {
269
+ int result = mt_fs_set_uv_error(out_error, &open_req, open_status, "fs read failed");
270
+ uv_fs_req_cleanup(&open_req);
271
+ return result;
272
+ }
273
+
274
+ uv_file descriptor = (uv_file) open_status;
275
+ uv_fs_req_cleanup(&open_req);
276
+
277
+ uintptr_t capacity = 0;
278
+ char* buffer_data = NULL;
279
+ const uintptr_t chunk_size = 65536;
280
+ intptr_t offset = 0;
281
+
282
+ while (true) {
283
+ uintptr_t needed = (uintptr_t) offset + chunk_size;
284
+ if (needed > capacity) {
285
+ uintptr_t new_capacity = capacity == 0 ? chunk_size : capacity;
286
+ while (new_capacity < needed) {
287
+ if (new_capacity > (UINTPTR_MAX / 2)) {
288
+ int close_status = mt_fs_close_file(descriptor, out_error, "fs read failed");
289
+ free(buffer_data);
290
+ return close_status != 0
291
+ ? close_status
292
+ : mt_fs_set_message(out_error, EOVERFLOW, "fs read failed", "file is too large");
293
+ }
294
+ new_capacity *= 2;
295
+ }
296
+
297
+ char* resized = (char*) realloc(buffer_data, (size_t) new_capacity);
298
+ if (resized == NULL) {
299
+ int close_status = mt_fs_close_file(descriptor, out_error, "fs read failed");
300
+ free(buffer_data);
301
+ return close_status != 0
302
+ ? close_status
303
+ : mt_fs_set_errno_error(out_error, ENOMEM, "fs read failed");
304
+ }
305
+
306
+ buffer_data = resized;
307
+ capacity = new_capacity;
308
+ }
309
+
310
+ uv_buf_t buffer = uv_buf_init(buffer_data + offset, (unsigned int) chunk_size);
311
+ uv_fs_t read_req;
312
+ int read_status = uv_fs_read(NULL, &read_req, descriptor, &buffer, 1, offset, NULL);
313
+ if (read_status < 0) {
314
+ int result = mt_fs_set_uv_error(out_error, &read_req, read_status, "fs read failed");
315
+ uv_fs_req_cleanup(&read_req);
316
+ mt_fs_close_file(descriptor, out_error, "fs read failed");
317
+ free(buffer_data);
318
+ return result;
319
+ }
320
+
321
+ uv_fs_req_cleanup(&read_req);
322
+ if (read_status == 0) {
323
+ break;
324
+ }
325
+
326
+ offset += (intptr_t) read_status;
327
+ if ((uintptr_t) offset > (UINTPTR_MAX - chunk_size)) {
328
+ int close_status = mt_fs_close_file(descriptor, out_error, "fs read failed");
329
+ free(buffer_data);
330
+ return close_status != 0
331
+ ? close_status
332
+ : mt_fs_set_message(out_error, EOVERFLOW, "fs read failed", "file is too large");
333
+ }
334
+ }
335
+
336
+ if (offset == 0) {
337
+ free(buffer_data);
338
+ buffer_data = NULL;
339
+ } else if ((uintptr_t) offset != capacity) {
340
+ char* sized = (char*) realloc(buffer_data, (size_t) offset);
341
+ if (sized != NULL) {
342
+ buffer_data = sized;
343
+ }
344
+ }
345
+
346
+ int close_status = mt_fs_close_file(descriptor, out_error, "fs read failed");
347
+ if (close_status != 0) {
348
+ free(buffer_data);
349
+ return close_status;
350
+ }
351
+
352
+ out_text->data = buffer_data;
353
+ out_text->len = (uintptr_t) offset;
354
+ return 0;
355
+ }
356
+
357
+ static inline int mt_fs_read_bytes(const char* path, mt_fs_string* out_bytes, mt_fs_error* out_error) {
358
+ return mt_fs_read_text(path, out_bytes, out_error);
359
+ }
360
+
361
+ static inline int mt_fs_write_text(const char* path, const char* data, uintptr_t len, mt_fs_error* out_error) {
362
+ mt_fs_reset_error(out_error);
363
+
364
+ if (path == NULL || path[0] == '\0') {
365
+ return mt_fs_set_message(out_error, -1, "fs write failed", "path cannot be empty");
366
+ }
367
+ if (len != 0 && data == NULL) {
368
+ return mt_fs_set_message(out_error, -1, "fs write failed", "missing input data");
369
+ }
370
+
371
+ uv_fs_t open_req;
372
+ int open_status = uv_fs_open(NULL, &open_req, path, O_WRONLY | O_CREAT | O_TRUNC, 0666, NULL);
373
+ if (open_status < 0) {
374
+ int result = mt_fs_set_uv_error(out_error, &open_req, open_status, "fs write failed");
375
+ uv_fs_req_cleanup(&open_req);
376
+ return result;
377
+ }
378
+
379
+ uv_file descriptor = (uv_file) open_status;
380
+ uv_fs_req_cleanup(&open_req);
381
+
382
+ uintptr_t written_total = 0;
383
+ intptr_t offset = 0;
384
+ while (written_total < len) {
385
+ uintptr_t remaining = len - written_total;
386
+ unsigned int chunk = remaining > 65536 ? 65536U : (unsigned int) remaining;
387
+ uv_buf_t buffer = uv_buf_init((char*) (data + written_total), chunk);
388
+ uv_fs_t write_req;
389
+ int write_status = uv_fs_write(NULL, &write_req, descriptor, &buffer, 1, offset, NULL);
390
+ if (write_status < 0) {
391
+ int result = mt_fs_set_uv_error(out_error, &write_req, write_status, "fs write failed");
392
+ uv_fs_req_cleanup(&write_req);
393
+ mt_fs_close_file(descriptor, out_error, "fs write failed");
394
+ return result;
395
+ }
396
+ if (write_status == 0) {
397
+ uv_fs_req_cleanup(&write_req);
398
+ mt_fs_close_file(descriptor, out_error, "fs write failed");
399
+ return mt_fs_set_message(out_error, EIO, "fs write failed", "short write");
400
+ }
401
+
402
+ uv_fs_req_cleanup(&write_req);
403
+ written_total += (uintptr_t) write_status;
404
+ offset += (intptr_t) write_status;
405
+ }
406
+
407
+ return mt_fs_close_file(descriptor, out_error, "fs write failed");
408
+ }
409
+
410
+ static inline int mt_fs_write_bytes(const char* path, const uint8_t* data, uintptr_t len, mt_fs_error* out_error) {
411
+ return mt_fs_write_text(path, (const char*) data, len, out_error);
412
+ }
413
+
414
+ static inline int mt_fs_remove(const char* path, mt_fs_error* out_error) {
415
+ mt_fs_reset_error(out_error);
416
+
417
+ if (path == NULL || path[0] == '\0') {
418
+ return mt_fs_set_message(out_error, -1, "fs remove failed", "path cannot be empty");
419
+ }
420
+
421
+ uv_fs_t req;
422
+ int status = uv_fs_unlink(NULL, &req, path, NULL);
423
+ if (status < 0) {
424
+ int unlink_code = status;
425
+ uv_fs_req_cleanup(&req);
426
+ if (unlink_code == UV_EISDIR || unlink_code == UV_EPERM) {
427
+ status = uv_fs_rmdir(NULL, &req, path, NULL);
428
+ if (status < 0) {
429
+ int result = mt_fs_set_uv_error(out_error, &req, status, "fs remove failed");
430
+ uv_fs_req_cleanup(&req);
431
+ return result;
432
+ }
433
+ uv_fs_req_cleanup(&req);
434
+ return 0;
435
+ }
436
+
437
+ return mt_fs_set_message(out_error, -unlink_code, "fs remove failed", uv_strerror(unlink_code));
438
+ }
439
+
440
+ uv_fs_req_cleanup(&req);
441
+ return 0;
442
+ }
443
+
444
+ static inline int mt_fs_rename(const char* source_path, const char* target_path, mt_fs_error* out_error) {
445
+ mt_fs_reset_error(out_error);
446
+
447
+ if (source_path == NULL || source_path[0] == '\0') {
448
+ return mt_fs_set_message(out_error, -1, "fs rename failed", "source path cannot be empty");
449
+ }
450
+ if (target_path == NULL || target_path[0] == '\0') {
451
+ return mt_fs_set_message(out_error, -1, "fs rename failed", "target path cannot be empty");
452
+ }
453
+
454
+ uv_fs_t req;
455
+ int status = uv_fs_rename(NULL, &req, source_path, target_path, NULL);
456
+ if (status < 0) {
457
+ int result = mt_fs_set_uv_error(out_error, &req, status, "fs rename failed");
458
+ uv_fs_req_cleanup(&req);
459
+ return result;
460
+ }
461
+
462
+ uv_fs_req_cleanup(&req);
463
+ return 0;
464
+ }
465
+
466
+ static inline int mt_fs_set_permissions(const char* path, int mode, mt_fs_error* out_error) {
467
+ mt_fs_reset_error(out_error);
468
+
469
+ if (path == NULL || path[0] == '\0') {
470
+ return mt_fs_set_message(out_error, -1, "fs set permissions failed", "path cannot be empty");
471
+ }
472
+
473
+ uv_fs_t req;
474
+ int status = uv_fs_chmod(NULL, &req, path, mode, NULL);
475
+ if (status < 0) {
476
+ int result = mt_fs_set_uv_error(out_error, &req, status, "fs set permissions failed");
477
+ uv_fs_req_cleanup(&req);
478
+ return result;
479
+ }
480
+
481
+ uv_fs_req_cleanup(&req);
482
+ return 0;
483
+ }
484
+
485
+ static inline int mt_fs_ensure_directory(const char* path, mt_fs_error* out_error) {
486
+ uv_fs_t mkdir_req;
487
+ int status = uv_fs_mkdir(NULL, &mkdir_req, path, 0777, NULL);
488
+ if (status == 0) {
489
+ uv_fs_req_cleanup(&mkdir_req);
490
+ return 0;
491
+ }
492
+
493
+ if (status == UV_EEXIST) {
494
+ uv_fs_req_cleanup(&mkdir_req);
495
+ uv_fs_t stat_req;
496
+ int stat_status = uv_fs_stat(NULL, &stat_req, path, NULL);
497
+ if (stat_status == 0) {
498
+ const uv_stat_t* info = uv_fs_get_statbuf(&stat_req);
499
+ bool is_directory = info != NULL && S_ISDIR(info->st_mode);
500
+ uv_fs_req_cleanup(&stat_req);
501
+ if (is_directory) {
502
+ return 0;
503
+ }
504
+ } else {
505
+ uv_fs_req_cleanup(&stat_req);
506
+ }
507
+ } else {
508
+ int result = mt_fs_set_uv_error(out_error, &mkdir_req, status, "fs create directories failed");
509
+ uv_fs_req_cleanup(&mkdir_req);
510
+ return result;
511
+ }
512
+
513
+ return mt_fs_set_message(out_error, EEXIST, "fs create directories failed", "path exists and is not a directory");
514
+ }
515
+
516
+ static inline int mt_fs_create_directories(const char* path, mt_fs_error* out_error) {
517
+ mt_fs_reset_error(out_error);
518
+
519
+ if (path == NULL || path[0] == '\0') {
520
+ return mt_fs_set_message(out_error, -1, "fs create directories failed", "path cannot be empty");
521
+ }
522
+
523
+ size_t len = strlen(path);
524
+ char* copy = (char*) malloc(len + 1);
525
+ if (copy == NULL) {
526
+ return mt_fs_set_errno_error(out_error, ENOMEM, "fs create directories failed");
527
+ }
528
+
529
+ memcpy(copy, path, len + 1);
530
+
531
+ int root = mt_fs_root_length(copy);
532
+ size_t index = (size_t) root;
533
+ while (copy[index] != '\0' && mt_fs_is_separator(copy[index])) {
534
+ index += 1;
535
+ }
536
+
537
+ while (true) {
538
+ while (copy[index] != '\0' && !mt_fs_is_separator(copy[index])) {
539
+ index += 1;
540
+ }
541
+
542
+ char saved = copy[index];
543
+ copy[index] = '\0';
544
+
545
+ if (copy[0] != '\0' && !(index == (size_t) root && root > 0)) {
546
+ int ensure_status = mt_fs_ensure_directory(copy, out_error);
547
+ if (ensure_status != 0) {
548
+ free(copy);
549
+ return ensure_status;
550
+ }
551
+ }
552
+
553
+ if (saved == '\0') {
554
+ break;
555
+ }
556
+
557
+ copy[index] = saved;
558
+ index += 1;
559
+ while (copy[index] != '\0' && mt_fs_is_separator(copy[index])) {
560
+ index += 1;
561
+ }
562
+ }
563
+
564
+ free(copy);
565
+ return 0;
566
+ }
567
+
568
+ static inline int mt_fs_current_directory(mt_fs_string* out_text, mt_fs_error* out_error) {
569
+ mt_fs_reset_string(out_text);
570
+ mt_fs_reset_error(out_error);
571
+
572
+ size_t capacity = 256;
573
+ while (true) {
574
+ char* buffer = (char*) malloc(capacity);
575
+ if (buffer == NULL) {
576
+ return mt_fs_set_errno_error(out_error, ENOMEM, "fs current directory failed");
577
+ }
578
+
579
+ size_t size = capacity;
580
+ int status = uv_cwd(buffer, &size);
581
+ if (status == 0) {
582
+ out_text->data = buffer;
583
+ out_text->len = (uintptr_t) size;
584
+ return 0;
585
+ }
586
+
587
+ free(buffer);
588
+ if (status != UV_ENOBUFS) {
589
+ return mt_fs_set_message(out_error, -status, "fs current directory failed", uv_strerror(status));
590
+ }
591
+
592
+ if (capacity > ((size_t) UINTPTR_MAX) / 2) {
593
+ return mt_fs_set_message(out_error, ENOMEM, "fs current directory failed", "path too long");
594
+ }
595
+
596
+ capacity *= 2;
597
+ }
598
+ }
599
+
600
+ static inline int mt_fs_temporary_directory(mt_fs_string* out_text, mt_fs_error* out_error) {
601
+ mt_fs_reset_string(out_text);
602
+ mt_fs_reset_error(out_error);
603
+
604
+ size_t capacity = 256;
605
+ while (true) {
606
+ char* buffer = (char*) malloc(capacity);
607
+ if (buffer == NULL) {
608
+ return mt_fs_set_errno_error(out_error, ENOMEM, "fs temporary directory failed");
609
+ }
610
+
611
+ size_t size = capacity;
612
+ int status = uv_os_tmpdir(buffer, &size);
613
+ if (status == 0) {
614
+ out_text->data = buffer;
615
+ out_text->len = (uintptr_t) size;
616
+ return 0;
617
+ }
618
+
619
+ free(buffer);
620
+ if (status != UV_ENOBUFS) {
621
+ return mt_fs_set_message(out_error, -status, "fs temporary directory failed", uv_strerror(status));
622
+ }
623
+
624
+ if (capacity > ((size_t) UINTPTR_MAX) / 2) {
625
+ return mt_fs_set_message(out_error, ENOMEM, "fs temporary directory failed", "path too long");
626
+ }
627
+
628
+ capacity *= 2;
629
+ }
630
+ }
631
+
632
+ static inline int mt_fs_canonicalize(const char* path, mt_fs_string* out_text, mt_fs_error* out_error) {
633
+ mt_fs_reset_string(out_text);
634
+ mt_fs_reset_error(out_error);
635
+
636
+ if (path == NULL || path[0] == '\0') {
637
+ return mt_fs_set_message(out_error, -1, "fs canonicalize failed", "path cannot be empty");
638
+ }
639
+
640
+ uv_fs_t req;
641
+ int status = uv_fs_realpath(NULL, &req, path, NULL);
642
+ if (status < 0) {
643
+ int result = mt_fs_set_uv_error(out_error, &req, status, "fs canonicalize failed");
644
+ uv_fs_req_cleanup(&req);
645
+ return result;
646
+ }
647
+
648
+ const char* resolved = (const char*) req.ptr;
649
+ if (resolved == NULL) {
650
+ uv_fs_req_cleanup(&req);
651
+ return mt_fs_set_message(out_error, EIO, "fs canonicalize failed", "missing realpath result");
652
+ }
653
+
654
+ size_t resolved_len = strlen(resolved);
655
+ char* copied = NULL;
656
+ if (resolved_len != 0) {
657
+ copied = (char*) malloc(resolved_len);
658
+ if (copied == NULL) {
659
+ uv_fs_req_cleanup(&req);
660
+ return mt_fs_set_errno_error(out_error, ENOMEM, "fs canonicalize failed");
661
+ }
662
+ memcpy(copied, resolved, resolved_len);
663
+ }
664
+
665
+ uv_fs_req_cleanup(&req);
666
+ out_text->data = copied;
667
+ out_text->len = (uintptr_t) resolved_len;
668
+ return 0;
669
+ }
670
+
671
+ static inline int mt_fs_create_temporary_directory(const char* parent_dir,
672
+ const char* prefix,
673
+ mt_fs_string* out_path,
674
+ mt_fs_error* out_error) {
675
+ mt_fs_reset_string(out_path);
676
+ mt_fs_reset_error(out_error);
677
+
678
+ if (parent_dir == NULL || parent_dir[0] == '\0') {
679
+ return mt_fs_set_message(out_error, -1, "fs create temporary directory failed", "parent directory cannot be empty");
680
+ }
681
+ if (prefix == NULL || prefix[0] == '\0') {
682
+ return mt_fs_set_message(out_error, -1, "fs create temporary directory failed", "prefix cannot be empty");
683
+ }
684
+
685
+ if (mt_fs_path_kind(parent_dir) != MT_FS_KIND_DIRECTORY) {
686
+ return mt_fs_set_message(out_error, ENOENT, "fs create temporary directory failed", "parent directory does not exist");
687
+ }
688
+
689
+ size_t parent_len = strlen(parent_dir);
690
+ size_t prefix_len = strlen(prefix);
691
+ bool needs_separator = parent_len != 0 && !mt_fs_is_separator(parent_dir[parent_len - 1]);
692
+ size_t template_len = parent_len + (needs_separator ? 1 : 0) + prefix_len + 7;
693
+
694
+ char* template_path = (char*) malloc(template_len + 1);
695
+ if (template_path == NULL) {
696
+ return mt_fs_set_errno_error(out_error, ENOMEM, "fs create temporary directory failed");
697
+ }
698
+
699
+ size_t offset = 0;
700
+ memcpy(template_path + offset, parent_dir, parent_len);
701
+ offset += parent_len;
702
+ if (needs_separator) {
703
+ template_path[offset] = '/';
704
+ offset += 1;
705
+ }
706
+ memcpy(template_path + offset, prefix, prefix_len);
707
+ offset += prefix_len;
708
+ memcpy(template_path + offset, "-XXXXXX", 7);
709
+ offset += 7;
710
+ template_path[offset] = '\0';
711
+
712
+ uv_fs_t mkdtemp_req;
713
+ int status = uv_fs_mkdtemp(NULL, &mkdtemp_req, template_path, NULL);
714
+ if (status < 0) {
715
+ int result = mt_fs_set_uv_error(out_error, &mkdtemp_req, status, "fs create temporary directory failed");
716
+ uv_fs_req_cleanup(&mkdtemp_req);
717
+ free(template_path);
718
+ return result;
719
+ }
720
+
721
+ const char* created_source = mkdtemp_req.path != NULL ? mkdtemp_req.path : template_path;
722
+ size_t created_len = strlen(created_source);
723
+ char* created_path = NULL;
724
+ if (created_len != 0) {
725
+ created_path = (char*) malloc(created_len);
726
+ if (created_path == NULL) {
727
+ uv_fs_req_cleanup(&mkdtemp_req);
728
+ free(template_path);
729
+ return mt_fs_set_errno_error(out_error, ENOMEM, "fs create temporary directory failed");
730
+ }
731
+ memcpy(created_path, created_source, created_len);
732
+ }
733
+
734
+ uv_fs_req_cleanup(&mkdtemp_req);
735
+ free(template_path);
736
+
737
+ out_path->data = created_path;
738
+ out_path->len = (uintptr_t) created_len;
739
+ return 0;
740
+ }
741
+
742
+ static inline int mt_fs_create_temporary_file(const char* parent_dir,
743
+ const char* prefix,
744
+ const char* suffix,
745
+ mt_fs_string* out_path,
746
+ mt_fs_error* out_error) {
747
+ mt_fs_reset_string(out_path);
748
+ mt_fs_reset_error(out_error);
749
+
750
+ if (parent_dir == NULL || parent_dir[0] == '\0') {
751
+ return mt_fs_set_message(out_error, -1, "fs create temporary file failed", "parent directory cannot be empty");
752
+ }
753
+ if (prefix == NULL || prefix[0] == '\0') {
754
+ return mt_fs_set_message(out_error, -1, "fs create temporary file failed", "prefix cannot be empty");
755
+ }
756
+
757
+ if (mt_fs_path_kind(parent_dir) != MT_FS_KIND_DIRECTORY) {
758
+ return mt_fs_set_message(out_error, ENOENT, "fs create temporary file failed", "parent directory does not exist");
759
+ }
760
+
761
+ size_t parent_len = strlen(parent_dir);
762
+ size_t prefix_len = strlen(prefix);
763
+ size_t suffix_len = suffix == NULL ? 0 : strlen(suffix);
764
+ bool needs_separator = parent_len != 0 && !mt_fs_is_separator(parent_dir[parent_len - 1]);
765
+ size_t template_len = parent_len + (needs_separator ? 1 : 0) + prefix_len + 7;
766
+
767
+ char* template_path = (char*) malloc(template_len + 1);
768
+ if (template_path == NULL) {
769
+ return mt_fs_set_errno_error(out_error, ENOMEM, "fs create temporary file failed");
770
+ }
771
+
772
+ size_t offset = 0;
773
+ memcpy(template_path + offset, parent_dir, parent_len);
774
+ offset += parent_len;
775
+ if (needs_separator) {
776
+ template_path[offset] = '/';
777
+ offset += 1;
778
+ }
779
+ memcpy(template_path + offset, prefix, prefix_len);
780
+ offset += prefix_len;
781
+ memcpy(template_path + offset, "-XXXXXX", 7);
782
+ offset += 7;
783
+ template_path[offset] = '\0';
784
+
785
+ uv_fs_t mkstemp_req;
786
+ int descriptor = uv_fs_mkstemp(NULL, &mkstemp_req, template_path, NULL);
787
+ if (descriptor < 0) {
788
+ int result = mt_fs_set_uv_error(out_error, &mkstemp_req, descriptor, "fs create temporary file failed");
789
+ uv_fs_req_cleanup(&mkstemp_req);
790
+ free(template_path);
791
+ return result;
792
+ }
793
+
794
+ const char* created_source = mkstemp_req.path != NULL ? mkstemp_req.path : template_path;
795
+ size_t created_len = strlen(created_source);
796
+ char* created_path = (char*) malloc(created_len + 1);
797
+ if (created_path == NULL) {
798
+ uv_fs_req_cleanup(&mkstemp_req);
799
+ mt_fs_close_file((uv_file) descriptor, out_error, "fs create temporary file failed");
800
+ uv_fs_t cleanup_req;
801
+ uv_fs_unlink(NULL, &cleanup_req, created_source, NULL);
802
+ uv_fs_req_cleanup(&cleanup_req);
803
+ free(template_path);
804
+ return mt_fs_set_errno_error(out_error, ENOMEM, "fs create temporary file failed");
805
+ }
806
+ memcpy(created_path, created_source, created_len + 1);
807
+
808
+ uv_fs_req_cleanup(&mkstemp_req);
809
+ free(template_path);
810
+
811
+ if (mt_fs_close_file((uv_file) descriptor, out_error, "fs create temporary file failed") != 0) {
812
+ uv_fs_t cleanup_req;
813
+ uv_fs_unlink(NULL, &cleanup_req, created_path, NULL);
814
+ uv_fs_req_cleanup(&cleanup_req);
815
+ free(created_path);
816
+ return out_error->code == 0 ? -1 : out_error->code;
817
+ }
818
+
819
+ if (suffix_len == 0) {
820
+ out_path->data = created_path;
821
+ out_path->len = (uintptr_t) created_len;
822
+ return 0;
823
+ }
824
+
825
+ size_t base_len = created_len;
826
+ size_t final_len = base_len + suffix_len;
827
+ char* final_path = (char*) malloc(final_len + 1);
828
+ if (final_path == NULL) {
829
+ uv_fs_t cleanup_req;
830
+ uv_fs_unlink(NULL, &cleanup_req, created_path, NULL);
831
+ uv_fs_req_cleanup(&cleanup_req);
832
+ free(created_path);
833
+ return mt_fs_set_errno_error(out_error, ENOMEM, "fs create temporary file failed");
834
+ }
835
+
836
+ memcpy(final_path, created_path, base_len);
837
+ memcpy(final_path + base_len, suffix, suffix_len);
838
+ final_path[final_len] = '\0';
839
+
840
+ uv_fs_t rename_req;
841
+ int rename_status = uv_fs_rename(NULL, &rename_req, created_path, final_path, NULL);
842
+ if (rename_status < 0) {
843
+ int result = mt_fs_set_uv_error(out_error, &rename_req, rename_status, "fs create temporary file failed");
844
+ uv_fs_req_cleanup(&rename_req);
845
+ uv_fs_t cleanup_req;
846
+ uv_fs_unlink(NULL, &cleanup_req, created_path, NULL);
847
+ uv_fs_req_cleanup(&cleanup_req);
848
+ free(created_path);
849
+ free(final_path);
850
+ return result;
851
+ }
852
+ uv_fs_req_cleanup(&rename_req);
853
+
854
+ free(created_path);
855
+ out_path->data = final_path;
856
+ out_path->len = (uintptr_t) final_len;
857
+ return 0;
858
+ }
859
+
860
+ static inline int mt_fs_list_entries(const char* path, mt_fs_entries* out_entries, mt_fs_error* out_error) {
861
+ mt_fs_reset_entries(out_entries);
862
+ mt_fs_reset_error(out_error);
863
+
864
+ if (path == NULL || path[0] == '\0') {
865
+ return mt_fs_set_message(out_error, -1, "fs list entries failed", "path cannot be empty");
866
+ }
867
+
868
+ uv_fs_t scandir_req;
869
+ int status = uv_fs_scandir(NULL, &scandir_req, path, 0, NULL);
870
+ if (status < 0) {
871
+ int result = mt_fs_set_uv_error(out_error, &scandir_req, status, "fs list entries failed");
872
+ uv_fs_req_cleanup(&scandir_req);
873
+ return result;
874
+ }
875
+
876
+ uintptr_t capacity = status > 0 ? (uintptr_t) status : 8;
877
+ uintptr_t count = 0;
878
+ char** data = NULL;
879
+ uintptr_t* lengths = NULL;
880
+
881
+ data = (char**) malloc((size_t) (capacity * sizeof(char*)));
882
+ lengths = (uintptr_t*) malloc((size_t) (capacity * sizeof(uintptr_t)));
883
+ if (data == NULL || lengths == NULL) {
884
+ free(data);
885
+ free(lengths);
886
+ uv_fs_req_cleanup(&scandir_req);
887
+ return mt_fs_set_errno_error(out_error, ENOMEM, "fs list entries failed");
888
+ }
889
+
890
+ while (true) {
891
+ uv_dirent_t entry;
892
+ int next_status = uv_fs_scandir_next(&scandir_req, &entry);
893
+ if (next_status == UV_EOF) {
894
+ break;
895
+ }
896
+ if (next_status < 0) {
897
+ mt_fs_release_entry_buffers(data, lengths, count);
898
+ uv_fs_req_cleanup(&scandir_req);
899
+ return mt_fs_set_message(out_error, -next_status, "fs list entries failed", uv_strerror(next_status));
900
+ }
901
+
902
+ if (entry.name == NULL) {
903
+ continue;
904
+ }
905
+ if ((strcmp(entry.name, ".") == 0) || (strcmp(entry.name, "..") == 0)) {
906
+ continue;
907
+ }
908
+
909
+ if (count == capacity) {
910
+ uintptr_t new_capacity = capacity * 2;
911
+ char** new_data = (char**) malloc((size_t) (new_capacity * sizeof(char*)));
912
+ uintptr_t* new_lengths = (uintptr_t*) malloc((size_t) (new_capacity * sizeof(uintptr_t)));
913
+ if (new_data == NULL || new_lengths == NULL) {
914
+ free(new_data);
915
+ free(new_lengths);
916
+ mt_fs_release_entry_buffers(data, lengths, count);
917
+ uv_fs_req_cleanup(&scandir_req);
918
+ return mt_fs_set_errno_error(out_error, ENOMEM, "fs list entries failed");
919
+ }
920
+
921
+ memcpy(new_data, data, (size_t) (count * sizeof(char*)));
922
+ memcpy(new_lengths, lengths, (size_t) (count * sizeof(uintptr_t)));
923
+ free(data);
924
+ free(lengths);
925
+ data = new_data;
926
+ lengths = new_lengths;
927
+ capacity = new_capacity;
928
+ }
929
+
930
+ size_t entry_len = strlen(entry.name);
931
+ char* copied = NULL;
932
+ if (entry_len != 0) {
933
+ copied = (char*) malloc(entry_len);
934
+ if (copied == NULL) {
935
+ mt_fs_release_entry_buffers(data, lengths, count);
936
+ uv_fs_req_cleanup(&scandir_req);
937
+ return mt_fs_set_errno_error(out_error, ENOMEM, "fs list entries failed");
938
+ }
939
+ memcpy(copied, entry.name, entry_len);
940
+ }
941
+
942
+ data[count] = copied;
943
+ lengths[count] = (uintptr_t) entry_len;
944
+ count += 1;
945
+ }
946
+
947
+ uv_fs_req_cleanup(&scandir_req);
948
+ out_entries->data = data;
949
+ out_entries->lengths = lengths;
950
+ out_entries->count = count;
951
+ return 0;
952
+ }
953
+
954
+ #endif