Mercurial > code
comparison modules/fs/fs.cpp @ 547:ecf5fb9319da
Fs: switch to spaces, add FS_EXPORT
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 15 Jun 2016 11:59:17 +0200 |
parents | d6dad57e9e6b |
children | 730358cb0648 |
comparison
equal
deleted
inserted
replaced
546:40c27081958a | 547:ecf5fb9319da |
---|---|
52 */ | 52 */ |
53 #if defined(_WIN32) | 53 #if defined(_WIN32) |
54 | 54 |
55 std::string error() | 55 std::string error() |
56 { | 56 { |
57 LPSTR error = nullptr; | 57 LPSTR error = nullptr; |
58 std::string errmsg = "Unknown error"; | 58 std::string errmsg = "Unknown error"; |
59 | 59 |
60 FormatMessageA( | 60 FormatMessageA( |
61 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, | 61 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, |
62 nullptr, | 62 nullptr, |
63 GetLastError(), | 63 GetLastError(), |
64 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | 64 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
65 (LPSTR)&error, 0, nullptr); | 65 (LPSTR)&error, 0, nullptr); |
66 | 66 |
67 if (error) { | 67 if (error) { |
68 errmsg = std::string(error); | 68 errmsg = std::string(error); |
69 LocalFree(error); | 69 LocalFree(error); |
70 } | 70 } |
71 | 71 |
72 return errmsg; | 72 return errmsg; |
73 } | 73 } |
74 | 74 |
75 #endif | 75 #endif |
76 | 76 |
77 /* | 77 /* |
80 * | 80 * |
81 * Check if we have access to the file specified, mode is the same used as std::fopen. | 81 * Check if we have access to the file specified, mode is the same used as std::fopen. |
82 */ | 82 */ |
83 bool hasAccess(const std::string &path, const std::string &mode) | 83 bool hasAccess(const std::string &path, const std::string &mode) |
84 { | 84 { |
85 assert(mode.length() == 1); | 85 assert(mode.length() == 1); |
86 assert(mode[0] == 'r' || mode[0] == 'w'); | 86 assert(mode[0] == 'r' || mode[0] == 'w'); |
87 | 87 |
88 auto fp = std::fopen(path.c_str(), mode.c_str()); | 88 auto fp = std::fopen(path.c_str(), mode.c_str()); |
89 | 89 |
90 if (fp == nullptr) | 90 if (fp == nullptr) |
91 return false; | 91 return false; |
92 | 92 |
93 std::fclose(fp); | 93 std::fclose(fp); |
94 | 94 |
95 return true; | 95 return true; |
96 } | 96 } |
97 | 97 |
98 /* | 98 /* |
99 * typeOf. | 99 * typeOf. |
100 * ------------------------------------------------------------------ | 100 * ------------------------------------------------------------------ |
108 #if defined(_WIN32) | 108 #if defined(_WIN32) |
109 | 109 |
110 template <typename Predicate> | 110 template <typename Predicate> |
111 bool typeOf(const std::string &path, Predicate &&predicate) | 111 bool typeOf(const std::string &path, Predicate &&predicate) |
112 { | 112 { |
113 DWORD result = GetFileAttributesA(path.c_str()); | 113 DWORD result = GetFileAttributesA(path.c_str()); |
114 | 114 |
115 if (result == INVALID_FILE_ATTRIBUTES) | 115 if (result == INVALID_FILE_ATTRIBUTES) |
116 return false; | 116 return false; |
117 | 117 |
118 return predicate(result); | 118 return predicate(result); |
119 } | 119 } |
120 | 120 |
121 #elif defined(FS_HAVE_STAT) | 121 #elif defined(FS_HAVE_STAT) |
122 | 122 |
123 template <typename Predicate> | 123 template <typename Predicate> |
124 bool typeOf(const std::string &path, Predicate &&predicate) noexcept | 124 bool typeOf(const std::string &path, Predicate &&predicate) noexcept |
125 { | 125 { |
126 struct stat st; | 126 struct stat st; |
127 | 127 |
128 if (::stat(path.c_str(), &st) < 0) | 128 if (::stat(path.c_str(), &st) < 0) |
129 return false; | 129 return false; |
130 | 130 |
131 return predicate(st); | 131 return predicate(st); |
132 } | 132 } |
133 | 133 |
134 #else | 134 #else |
135 | 135 |
136 template <typename Predicate> | 136 template <typename Predicate> |
137 bool typeOf(const std::string &path, Predicate &&predicate) noexcept | 137 bool typeOf(const std::string &path, Predicate &&predicate) noexcept |
138 { | 138 { |
139 throw std::runtime_error(std::strerror(ENOSYS)); | 139 throw std::runtime_error(std::strerror(ENOSYS)); |
140 } | 140 } |
141 | 141 |
142 #endif | 142 #endif |
143 | 143 |
144 } // !namespace | 144 } // !namespace |
147 * clean. | 147 * clean. |
148 * ------------------------------------------------------------------ | 148 * ------------------------------------------------------------------ |
149 */ | 149 */ |
150 std::string clean(std::string input) | 150 std::string clean(std::string input) |
151 { | 151 { |
152 if (input.empty()) | 152 if (input.empty()) |
153 return input; | 153 return input; |
154 | 154 |
155 /* First, remove any duplicates */ | 155 // First, remove any duplicates. |
156 input.erase(std::unique(input.begin(), input.end(), [&] (char c1, char c2) { | 156 input.erase(std::unique(input.begin(), input.end(), [&] (char c1, char c2) { |
157 return c1 == c2 && (c1 == '/' || c1 == '\\'); | 157 return c1 == c2 && (c1 == '/' || c1 == '\\'); |
158 }), input.end()); | 158 }), input.end()); |
159 | 159 |
160 /* Add a trailing / or \\ */ | 160 // Add a trailing / or \\. |
161 char c = input[input.length() - 1]; | 161 char c = input[input.length() - 1]; |
162 | 162 |
163 if (c != '/' && c != '\\') | 163 if (c != '/' && c != '\\') |
164 input += separator(); | 164 input += separator(); |
165 | 165 |
166 /* Now converts all / to \\ for Windows and the opposite for Unix */ | 166 // Now converts all / to \\ for Windows and the opposite for Unix. |
167 #if defined(_WIN32) | 167 #if defined(_WIN32) |
168 std::replace(input.begin(), input.end(), '/', '\\'); | 168 std::replace(input.begin(), input.end(), '/', '\\'); |
169 #else | 169 #else |
170 std::replace(input.begin(), input.end(), '\\', '/'); | 170 std::replace(input.begin(), input.end(), '\\', '/'); |
171 #endif | 171 #endif |
172 | 172 |
173 return input; | 173 return input; |
174 } | 174 } |
175 | 175 |
176 /* | 176 /* |
177 * baseName. | 177 * baseName. |
178 * ------------------------------------------------------------------ | 178 * ------------------------------------------------------------------ |
179 */ | 179 */ |
180 std::string baseName(std::string path) | 180 std::string baseName(std::string path) |
181 { | 181 { |
182 auto pos = path.find_last_of("\\/"); | 182 auto pos = path.find_last_of("\\/"); |
183 | 183 |
184 if (pos != std::string::npos) | 184 if (pos != std::string::npos) |
185 path = path.substr(pos + 1); | 185 path = path.substr(pos + 1); |
186 | 186 |
187 return path; | 187 return path; |
188 } | 188 } |
189 | 189 |
190 /* | 190 /* |
191 * dirName. | 191 * dirName. |
192 * ------------------------------------------------------------------ | 192 * ------------------------------------------------------------------ |
193 */ | 193 */ |
194 std::string dirName(std::string path) | 194 std::string dirName(std::string path) |
195 { | 195 { |
196 auto pos = path.find_last_of("\\/"); | 196 auto pos = path.find_last_of("\\/"); |
197 | 197 |
198 if (pos == std::string::npos) | 198 if (pos == std::string::npos) |
199 path = "."; | 199 path = "."; |
200 else | 200 else |
201 path = path.substr(0, pos); | 201 path = path.substr(0, pos); |
202 | 202 |
203 return path; | 203 return path; |
204 } | 204 } |
205 | 205 |
206 /* | 206 /* |
207 * isAbsolute. | 207 * isAbsolute. |
208 * ------------------------------------------------------------------ | 208 * ------------------------------------------------------------------ |
209 */ | 209 */ |
210 bool isAbsolute(const std::string &path) noexcept | 210 bool isAbsolute(const std::string &path) noexcept |
211 { | 211 { |
212 #if defined(_WIN32) | 212 #if defined(_WIN32) |
213 return !isRelative(path); | 213 return !isRelative(path); |
214 #else | 214 #else |
215 return path.size() > 0 && path[0] == '/'; | 215 return path.size() > 0 && path[0] == '/'; |
216 #endif | 216 #endif |
217 } | 217 } |
218 | 218 |
219 /* | 219 /* |
220 * isRelative. | 220 * isRelative. |
221 * ------------------------------------------------------------------ | 221 * ------------------------------------------------------------------ |
222 */ | 222 */ |
223 bool isRelative(const std::string &path) noexcept | 223 bool isRelative(const std::string &path) noexcept |
224 { | 224 { |
225 #if defined(_WIN32) | 225 #if defined(_WIN32) |
226 return PathIsRelativeA(path.c_str()) == 1; | 226 return PathIsRelativeA(path.c_str()) == 1; |
227 #else | 227 #else |
228 return !isAbsolute(path); | 228 return !isAbsolute(path); |
229 #endif | 229 #endif |
230 } | 230 } |
231 | 231 |
232 /* | 232 /* |
233 * isReadable. | 233 * isReadable. |
234 * ------------------------------------------------------------------ | 234 * ------------------------------------------------------------------ |
235 */ | 235 */ |
236 bool isReadable(const std::string &path) noexcept | 236 bool isReadable(const std::string &path) noexcept |
237 { | 237 { |
238 return hasAccess(path, "r"); | 238 return hasAccess(path, "r"); |
239 } | 239 } |
240 | 240 |
241 /* | 241 /* |
242 * isWritable. | 242 * isWritable. |
243 * ------------------------------------------------------------------ | 243 * ------------------------------------------------------------------ |
244 */ | 244 */ |
245 bool isWritable(const std::string &path) noexcept | 245 bool isWritable(const std::string &path) noexcept |
246 { | 246 { |
247 return hasAccess(path, "w"); | 247 return hasAccess(path, "w"); |
248 } | 248 } |
249 | 249 |
250 /* | 250 /* |
251 * isFile. | 251 * isFile. |
252 * ------------------------------------------------------------------ | 252 * ------------------------------------------------------------------ |
253 */ | 253 */ |
254 bool isFile(const std::string &path) | 254 bool isFile(const std::string &path) |
255 { | 255 { |
256 return typeOf(path, [] (const auto &object) { | 256 return typeOf(path, [] (const auto &object) { |
257 #if defined(_WIN32) | 257 #if defined(_WIN32) |
258 return (object & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE; | 258 return (object & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE; |
259 #elif defined(FS_HAVE_STAT) | 259 #elif defined(FS_HAVE_STAT) |
260 return S_ISREG(object.st_mode); | 260 return S_ISREG(object.st_mode); |
261 #endif | 261 #endif |
262 }); | 262 }); |
263 } | 263 } |
264 | 264 |
265 /* | 265 /* |
266 * isDirectory. | 266 * isDirectory. |
267 * ------------------------------------------------------------------ | 267 * ------------------------------------------------------------------ |
268 */ | 268 */ |
269 bool isDirectory(const std::string &path) | 269 bool isDirectory(const std::string &path) |
270 { | 270 { |
271 return typeOf(path, [] (const auto &object) { | 271 return typeOf(path, [] (const auto &object) { |
272 #if defined(_WIN32) | 272 #if defined(_WIN32) |
273 return (object & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; | 273 return (object & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; |
274 #elif defined(FS_HAVE_STAT) | 274 #elif defined(FS_HAVE_STAT) |
275 return S_ISDIR(object.st_mode); | 275 return S_ISDIR(object.st_mode); |
276 #endif | 276 #endif |
277 }); | 277 }); |
278 } | 278 } |
279 | 279 |
280 /* | 280 /* |
281 * isSymlink. | 281 * isSymlink. |
282 * ------------------------------------------------------------------ | 282 * ------------------------------------------------------------------ |
283 */ | 283 */ |
284 bool isSymlink(const std::string &path) | 284 bool isSymlink(const std::string &path) |
285 { | 285 { |
286 return typeOf(path, [] (const auto &object) { | 286 return typeOf(path, [] (const auto &object) { |
287 #if defined(_WIN32) | 287 #if defined(_WIN32) |
288 return (object & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT; | 288 return (object & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT; |
289 #elif defined(FS_HAVE_STAT) | 289 #elif defined(FS_HAVE_STAT) |
290 return S_ISLNK(object.st_mode); | 290 return S_ISLNK(object.st_mode); |
291 #endif | 291 #endif |
292 }); | 292 }); |
293 } | 293 } |
294 | 294 |
295 /* | 295 /* |
296 * stat. | 296 * stat. |
297 * ------------------------------------------------------------------ | 297 * ------------------------------------------------------------------ |
298 */ | 298 */ |
299 #if defined(FS_HAVE_STAT) | 299 #if defined(FS_HAVE_STAT) |
300 | 300 |
301 struct stat stat(const std::string &path) | 301 struct stat stat(const std::string &path) |
302 { | 302 { |
303 struct stat st; | 303 struct stat st; |
304 | 304 |
305 if (::stat(path.c_str(), &st) < 0) | 305 if (::stat(path.c_str(), &st) < 0) |
306 throw std::runtime_error(std::strerror(errno)); | 306 throw std::runtime_error(std::strerror(errno)); |
307 | 307 |
308 return st; | 308 return st; |
309 } | 309 } |
310 | 310 |
311 #endif | 311 #endif |
312 | 312 |
313 /* | 313 /* |
315 * ------------------------------------------------------------------ | 315 * ------------------------------------------------------------------ |
316 */ | 316 */ |
317 bool exists(const std::string &path) noexcept | 317 bool exists(const std::string &path) noexcept |
318 { | 318 { |
319 #if defined(FS_HAVE_STAT) | 319 #if defined(FS_HAVE_STAT) |
320 struct stat st; | 320 struct stat st; |
321 | 321 |
322 return ::stat(path.c_str(), &st) == 0; | 322 return ::stat(path.c_str(), &st) == 0; |
323 #else | 323 #else |
324 return hasAccess(path, "r"); | 324 return hasAccess(path, "r"); |
325 #endif | 325 #endif |
326 } | 326 } |
327 | 327 |
328 /* | 328 /* |
329 * readdir. | 329 * readdir. |
330 * ------------------------------------------------------------------ | 330 * ------------------------------------------------------------------ |
331 */ | 331 */ |
332 std::vector<Entry> readdir(const std::string &path, int flags) | 332 std::vector<Entry> readdir(const std::string &path, int flags) |
333 { | 333 { |
334 std::vector<Entry> entries; | 334 std::vector<Entry> entries; |
335 | 335 |
336 #if defined(_WIN32) | 336 #if defined(_WIN32) |
337 std::ostringstream oss; | 337 std::ostringstream oss; |
338 HANDLE handle; | 338 HANDLE handle; |
339 WIN32_FIND_DATA fdata; | 339 WIN32_FIND_DATA fdata; |
340 | 340 |
341 oss << path << "\\*"; | 341 oss << path << "\\*"; |
342 handle = FindFirstFile(oss.str().c_str(), &fdata); | 342 handle = FindFirstFile(oss.str().c_str(), &fdata); |
343 | 343 |
344 if (handle == nullptr) | 344 if (handle == nullptr) |
345 throw std::runtime_error(error()); | 345 throw std::runtime_error(error()); |
346 | 346 |
347 do { | 347 do { |
348 Entry entry; | 348 Entry entry; |
349 | 349 |
350 entry.name = fdata.cFileName; | 350 entry.name = fdata.cFileName; |
351 | 351 |
352 if (entry.name == "." && !(flags & Dot)) | 352 if (entry.name == "." && !(flags & Dot)) |
353 continue; | 353 continue; |
354 if (entry.name == ".." && !(flags & DotDot)) | 354 if (entry.name == ".." && !(flags & DotDot)) |
355 continue; | 355 continue; |
356 | 356 |
357 switch (fdata.dwFileAttributes) { | 357 switch (fdata.dwFileAttributes) { |
358 case FILE_ATTRIBUTE_DIRECTORY: | 358 case FILE_ATTRIBUTE_DIRECTORY: |
359 entry.type = Entry::Dir; | 359 entry.type = Entry::Dir; |
360 break; | 360 break; |
361 case FILE_ATTRIBUTE_NORMAL: | 361 case FILE_ATTRIBUTE_NORMAL: |
362 entry.type = Entry::File; | 362 entry.type = Entry::File; |
363 break; | 363 break; |
364 case FILE_ATTRIBUTE_REPARSE_POINT: | 364 case FILE_ATTRIBUTE_REPARSE_POINT: |
365 entry.type = Entry::Link; | 365 entry.type = Entry::Link; |
366 break; | 366 break; |
367 default: | 367 default: |
368 break; | 368 break; |
369 } | 369 } |
370 | 370 |
371 entries.push_back(std::move(entry)); | 371 entries.push_back(std::move(entry)); |
372 } while (FindNextFile(handle, &fdata) != 0); | 372 } while (FindNextFile(handle, &fdata) != 0); |
373 | 373 |
374 FindClose(handle); | 374 FindClose(handle); |
375 #else | 375 #else |
376 DIR *dp; | 376 DIR *dp; |
377 struct dirent *ent; | 377 struct dirent *ent; |
378 | 378 |
379 if ((dp = opendir(path.c_str())) == nullptr) | 379 if ((dp = opendir(path.c_str())) == nullptr) |
380 throw std::runtime_error(std::strerror(errno)); | 380 throw std::runtime_error(std::strerror(errno)); |
381 | 381 |
382 while ((ent = readdir(dp)) != nullptr) { | 382 while ((ent = readdir(dp)) != nullptr) { |
383 Entry entry; | 383 Entry entry; |
384 | 384 |
385 entry.name = ent->d_name; | 385 entry.name = ent->d_name; |
386 if (entry.name == "." && !(flags & Dot)) | 386 if (entry.name == "." && !(flags & Dot)) |
387 continue; | 387 continue; |
388 if (entry.name == ".." && !(flags & DotDot)) | 388 if (entry.name == ".." && !(flags & DotDot)) |
389 continue; | 389 continue; |
390 | 390 |
391 switch (ent->d_type) { | 391 switch (ent->d_type) { |
392 case DT_DIR: | 392 case DT_DIR: |
393 entry.type = Entry::Dir; | 393 entry.type = Entry::Dir; |
394 break; | 394 break; |
395 case DT_REG: | 395 case DT_REG: |
396 entry.type = Entry::File; | 396 entry.type = Entry::File; |
397 break; | 397 break; |
398 case DT_LNK: | 398 case DT_LNK: |
399 entry.type = Entry::Link; | 399 entry.type = Entry::Link; |
400 break; | 400 break; |
401 default: | 401 default: |
402 break; | 402 break; |
403 } | 403 } |
404 | 404 |
405 entries.push_back(std::move(entry)); | 405 entries.push_back(std::move(entry)); |
406 } | 406 } |
407 | 407 |
408 closedir(dp); | 408 closedir(dp); |
409 #endif | 409 #endif |
410 | 410 |
411 return entries; | 411 return entries; |
412 } | 412 } |
413 | 413 |
414 /* | 414 /* |
415 * mkdir. | 415 * mkdir. |
416 * ------------------------------------------------------------------ | 416 * ------------------------------------------------------------------ |
417 */ | 417 */ |
418 void mkdir(const std::string &path, int mode) | 418 void mkdir(const std::string &path, int mode) |
419 { | 419 { |
420 std::string::size_type next = 0; | 420 std::string::size_type next = 0; |
421 std::string part; | 421 std::string part; |
422 | 422 |
423 for (;;) { | 423 for (;;) { |
424 next = path.find_first_of("\\/", next); | 424 next = path.find_first_of("\\/", next); |
425 part = path.substr(0, next); | 425 part = path.substr(0, next); |
426 | 426 |
427 if (!part.empty()) { | 427 if (!part.empty()) { |
428 #if defined(_WIN32) | 428 #if defined(_WIN32) |
429 (void)mode; | 429 (void)mode; |
430 | 430 |
431 if (::_mkdir(part.c_str()) < 0 && errno != EEXIST) | 431 if (::_mkdir(part.c_str()) < 0 && errno != EEXIST) |
432 throw std::runtime_error(std::strerror(errno)); | 432 throw std::runtime_error(std::strerror(errno)); |
433 #else | 433 #else |
434 if (::mkdir(part.c_str(), mode) < 0 && errno != EEXIST) | 434 if (::mkdir(part.c_str(), mode) < 0 && errno != EEXIST) |
435 throw std::runtime_error(std::strerror(errno)); | 435 throw std::runtime_error(std::strerror(errno)); |
436 #endif | 436 #endif |
437 } | 437 } |
438 | 438 |
439 if (next++ == std::string::npos) | 439 if (next++ == std::string::npos) |
440 break; | 440 break; |
441 } | 441 } |
442 } | 442 } |
443 | 443 |
444 /* | 444 /* |
445 * rmdir. | 445 * rmdir. |
446 * ------------------------------------------------------------------ | 446 * ------------------------------------------------------------------ |
447 */ | 447 */ |
448 void rmdir(const std::string &base) noexcept | 448 void rmdir(const std::string &base) noexcept |
449 { | 449 { |
450 try { | 450 try { |
451 for (const auto &entry : readdir(base)) { | 451 for (const auto &entry : readdir(base)) { |
452 std::string path = base + separator() + entry.name; | 452 std::string path = base + separator() + entry.name; |
453 | 453 |
454 if (entry.type == Entry::Dir) | 454 if (entry.type == Entry::Dir) |
455 rmdir(path); | 455 rmdir(path); |
456 else | 456 else |
457 remove(path.c_str()); | 457 ::remove(path.c_str()); |
458 } | 458 } |
459 } catch (...) { | 459 } catch (...) { |
460 /* Silently discard to remove as much as possible */ | 460 // Silently discard to remove as much as possible. |
461 } | 461 } |
462 | 462 |
463 #if defined(_WIN32) | 463 #if defined(_WIN32) |
464 RemoveDirectoryA(base.c_str()); | 464 ::RemoveDirectoryA(base.c_str()); |
465 #else | 465 #else |
466 remove(base.c_str()); | 466 ::remove(base.c_str()); |
467 #endif | 467 #endif |
468 } | 468 } |
469 | 469 |
470 /* | 470 /* |
471 * cwd. | 471 * cwd. |
472 * ------------------------------------------------------------------ | 472 * ------------------------------------------------------------------ |
473 */ | 473 */ |
474 std::string cwd() | 474 std::string cwd() |
475 { | 475 { |
476 #if defined(_WIN32) | 476 #if defined(_WIN32) |
477 char path[MAX_PATH]; | 477 char path[MAX_PATH]; |
478 | 478 |
479 if (!GetCurrentDirectoryA(sizeof (path), path)) | 479 if (!::GetCurrentDirectoryA(sizeof (path), path)) |
480 throw std::runtime_error("failed to get current working directory"); | 480 throw std::runtime_error("failed to get current working directory"); |
481 | 481 |
482 return path; | 482 return path; |
483 #else | 483 #else |
484 char path[PATH_MAX]; | 484 char path[PATH_MAX]; |
485 | 485 |
486 if (getcwd(path, sizeof (path)) == nullptr) | 486 if (::getcwd(path, sizeof (path)) == nullptr) |
487 throw std::runtime_error{std::strerror(errno)}; | 487 throw std::runtime_error{std::strerror(errno)}; |
488 | 488 |
489 return path; | 489 return path; |
490 #endif | 490 #endif |
491 } | 491 } |
492 | 492 |
493 } // !fs | 493 } // !fs |