PageRenderTime 56ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/Ruby/Libraries/Builtins/FileOps.cs

http://github.com/IronLanguages/main
C# | 1217 lines | 971 code | 206 blank | 40 comment | 160 complexity | a58887ead2b9d2edb36fbbdf2e0b3155 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception

Large files files are truncated, but you can click here to view the full file

  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.IO;
  20. using System.Runtime.InteropServices;
  21. using System.Text;
  22. using IronRuby.Runtime;
  23. using IronRuby.Runtime.Calls;
  24. using Microsoft.Scripting;
  25. using Microsoft.Scripting.Runtime;
  26. using IronRuby.Compiler;
  27. using System.Globalization;
  28. using IronRuby.Runtime.Conversions;
  29. using System.Runtime.CompilerServices;
  30. using System.Reflection;
  31. using Microsoft.Scripting.Utils;
  32. namespace IronRuby.Builtins {
  33. /// <summary>
  34. /// File builtin class. Derives from IO
  35. /// </summary>
  36. [RubyClass("File", Extends = typeof(RubyFile))]
  37. public static class RubyFileOps {
  38. internal static bool FileExists(RubyContext/*!*/ context, MutableString/*!*/ path) {
  39. return context.Platform.FileExists(context.DecodePath(path));
  40. }
  41. internal static bool DirectoryExists(RubyContext/*!*/ context, MutableString/*!*/ path) {
  42. return context.Platform.DirectoryExists(context.DecodePath(path));
  43. }
  44. internal static bool Exists(RubyContext/*!*/ context, MutableString/*!*/ path) {
  45. var strPath = context.DecodePath(path);
  46. return context.Platform.DirectoryExists(strPath) || context.Platform.FileExists(strPath);
  47. }
  48. #region Construction
  49. [RubyConstructor]
  50. public static RubyFile/*!*/ CreateFile(
  51. ConversionStorage<int?>/*!*/ toInt,
  52. ConversionStorage<IDictionary<object, object>>/*!*/ toHash,
  53. ConversionStorage<MutableString>/*!*/ toPath,
  54. ConversionStorage<MutableString>/*!*/ toStr,
  55. RubyClass/*!*/ self,
  56. object descriptorOrPath,
  57. [Optional]object optionsOrMode,
  58. [Optional]object optionsOrPermissions,
  59. [DefaultParameterValue(null), DefaultProtocol]IDictionary<object, object> options) {
  60. return Reinitialize(toInt, toHash, toPath, toStr, new RubyFile(self.Context), descriptorOrPath, optionsOrMode, optionsOrPermissions, options);
  61. }
  62. [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)]
  63. public static RubyFile/*!*/ Reinitialize(
  64. ConversionStorage<int?>/*!*/ toInt,
  65. ConversionStorage<IDictionary<object, object>>/*!*/ toHash,
  66. ConversionStorage<MutableString>/*!*/ toPath,
  67. ConversionStorage<MutableString>/*!*/ toStr,
  68. RubyFile/*!*/ self,
  69. object descriptorOrPath,
  70. [Optional]object optionsOrMode,
  71. [Optional]object optionsOrPermissions,
  72. [DefaultParameterValue(null), DefaultProtocol]IDictionary<object, object> options) {
  73. var context = self.Context;
  74. Protocols.TryConvertToOptions(toHash, ref options, ref optionsOrMode, ref optionsOrPermissions);
  75. var toIntSite = toInt.GetSite(TryConvertToFixnumAction.Make(toInt.Context));
  76. IOInfo info = new IOInfo();
  77. if (optionsOrMode != Missing.Value) {
  78. int? m = toIntSite.Target(toIntSite, optionsOrMode);
  79. info = m.HasValue ? new IOInfo((IOMode)m) : IOInfo.Parse(context, Protocols.CastToString(toStr, optionsOrMode));
  80. }
  81. int permissions = 0;
  82. if (optionsOrPermissions != Missing.Value) {
  83. int? p = toIntSite.Target(toIntSite, optionsOrPermissions);
  84. if (!p.HasValue) {
  85. throw RubyExceptions.CreateTypeConversionError(context.GetClassName(optionsOrPermissions), "Integer");
  86. }
  87. permissions = p.Value;
  88. }
  89. if (options != null) {
  90. info = info.AddOptions(toStr, options);
  91. }
  92. // TODO: permissions
  93. // descriptor or path:
  94. int? descriptor = toIntSite.Target(toIntSite, descriptorOrPath);
  95. if (descriptor.HasValue) {
  96. RubyIOOps.Reinitialize(self, descriptor.Value, info);
  97. } else {
  98. Reinitialize(self, Protocols.CastToPath(toPath, descriptorOrPath), info, permissions);
  99. }
  100. return self;
  101. }
  102. private static void Reinitialize(RubyFile/*!*/ file, MutableString/*!*/ path, IOInfo info, int permission) {
  103. var strPath = file.Context.DecodePath(path);
  104. var stream = RubyFile.OpenFileStream(file.Context, strPath, info.Mode);
  105. file.Path = strPath;
  106. file.Mode = info.Mode;
  107. file.SetStream(stream);
  108. file.SetFileDescriptor(file.Context.AllocateFileDescriptor(stream));
  109. if (info.HasEncoding) {
  110. file.ExternalEncoding = info.ExternalEncoding;
  111. file.InternalEncoding = info.InternalEncoding;
  112. }
  113. }
  114. #endregion
  115. #region Declared Constants
  116. static RubyFileOps() {
  117. ALT_SEPARATOR = MutableString.CreateAscii(AltDirectorySeparatorChar.ToString()).Freeze();
  118. SEPARATOR = MutableString.CreateAscii(DirectorySeparatorChar.ToString()).Freeze();
  119. Separator = SEPARATOR;
  120. PATH_SEPARATOR = MutableString.CreateAscii(PathSeparatorChar.ToString()).Freeze();
  121. }
  122. private const char AltDirectorySeparatorChar = '\\';
  123. private const char DirectorySeparatorChar = '/';
  124. private const char PathSeparatorChar = ';';
  125. internal static bool IsDirectorySeparator(int c) {
  126. return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
  127. }
  128. [RubyConstant]
  129. public readonly static MutableString ALT_SEPARATOR;
  130. [RubyConstant]
  131. public readonly static MutableString PATH_SEPARATOR;
  132. [RubyConstant]
  133. public readonly static MutableString SEPARATOR;
  134. [RubyConstant]
  135. public readonly static MutableString Separator = SEPARATOR;
  136. private const string NUL_VALUE = "NUL";
  137. [RubyModule("Constants")]
  138. public static class Constants {
  139. [RubyConstant]
  140. public readonly static int APPEND = (int)IOMode.WriteAppends;
  141. [RubyConstant]
  142. public readonly static int BINARY = (int)IOMode.PreserveEndOfLines;
  143. [RubyConstant]
  144. public readonly static int CREAT = (int)IOMode.CreateIfNotExists;
  145. [RubyConstant]
  146. public readonly static int EXCL = (int)IOMode.ErrorIfExists;
  147. [RubyConstant]
  148. public readonly static int FNM_CASEFOLD = 0x08;
  149. [RubyConstant]
  150. public readonly static int FNM_DOTMATCH = 0x04;
  151. [RubyConstant]
  152. public readonly static int FNM_NOESCAPE = 0x01;
  153. [RubyConstant]
  154. public readonly static int FNM_PATHNAME = 0x02;
  155. [RubyConstant]
  156. public readonly static int FNM_SYSCASE = 0x08;
  157. [RubyConstant]
  158. public readonly static int LOCK_EX = 0x02;
  159. [RubyConstant]
  160. public readonly static int LOCK_NB = 0x04;
  161. [RubyConstant]
  162. public readonly static int LOCK_SH = 0x01;
  163. [RubyConstant]
  164. public readonly static int LOCK_UN = 0x08;
  165. [RubyConstant]
  166. public readonly static int NONBLOCK = (int)IOMode.WriteOnly;
  167. [RubyConstant]
  168. public readonly static int RDONLY = (int)IOMode.ReadOnly;
  169. [RubyConstant]
  170. public readonly static int RDWR = (int)IOMode.ReadWrite;
  171. [RubyConstant]
  172. public readonly static int TRUNC = (int)IOMode.Truncate;
  173. [RubyConstant]
  174. public readonly static int WRONLY = (int)IOMode.WriteOnly;
  175. }
  176. #endregion
  177. internal const int WriteModeMask = 0x80; // Oct 0200
  178. internal const int ReadWriteMode = 0x1B6; // Oct 0666
  179. [RubyMethod("open", RubyMethodAttributes.PublicSingleton)]
  180. public static RuleGenerator/*!*/ Open() {
  181. return RubyIOOps.Open();
  182. }
  183. #region chmod, chown, lchmod, lchown, umask
  184. [RubyMethod("chmod")]
  185. public static int Chmod(RubyFile/*!*/ self, [DefaultProtocol]int permission) {
  186. self.RequireInitialized();
  187. // TODO:
  188. if (self.Path == null) {
  189. throw new NotSupportedException("TODO: cannot chmod for files without path");
  190. }
  191. Chmod(self.Path, permission);
  192. return 0;
  193. }
  194. [RubyMethod("chmod", RubyMethodAttributes.PublicSingleton)]
  195. public static int Chmod(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, [DefaultProtocol]int permission, object path) {
  196. Chmod(self.Context.DecodePath(Protocols.CastToPath(toPath, path)), permission);
  197. return 1;
  198. }
  199. internal static void Chmod(string/*!*/ path, int permission) {
  200. #if FEATURE_FILESYSTEM
  201. FileAttributes oldAttributes = File.GetAttributes(path);
  202. if ((permission & WriteModeMask) == 0) {
  203. File.SetAttributes(path, oldAttributes | FileAttributes.ReadOnly);
  204. } else {
  205. File.SetAttributes(path, oldAttributes & ~FileAttributes.ReadOnly);
  206. }
  207. #endif
  208. }
  209. [RubyMethod("chown")]
  210. public static int ChangeOwner(RubyFile/*!*/ self, [DefaultProtocol]int owner, [DefaultProtocol]int group) {
  211. return 0;
  212. }
  213. [RubyMethod("chown")]
  214. public static int ChangeOwner(RubyContext/*!*/ context, RubyFile/*!*/ self, object owner, object group) {
  215. if ((owner == null || owner is int) && (group == null || group is int)) {
  216. return 0;
  217. }
  218. throw RubyExceptions.CreateUnexpectedTypeError(context, owner, "Fixnum");
  219. }
  220. [RubyMethod("chown", RubyMethodAttributes.PublicSingleton)]
  221. public static int ChangeOwner(RubyClass/*!*/ self, [DefaultProtocol]int owner, [DefaultProtocol]int group, [DefaultProtocol, NotNull]MutableString/*!*/ path) {
  222. return 0;
  223. }
  224. [RubyMethod("chown", RubyMethodAttributes.PublicSingleton)]
  225. public static int ChangeOwner(RubyContext/*!*/ context, RubyClass/*!*/ self, object owner, object group, [DefaultProtocol, NotNull]MutableString/*!*/ path) {
  226. if ((owner == null || owner is int) && (group == null || group is int)) {
  227. return 0;
  228. }
  229. throw RubyExceptions.CreateUnexpectedTypeError(context, owner, "Fixnum");
  230. }
  231. //lchmod
  232. //lchown
  233. internal static readonly object UmaskKey = new object();
  234. [RubyMethod("umask", RubyMethodAttributes.PublicSingleton)]
  235. public static int GetUmask(RubyClass/*!*/ self, [DefaultProtocol]int mask) {
  236. int result = (int)self.Context.GetOrCreateLibraryData(UmaskKey, () => 0);
  237. self.Context.TrySetLibraryData(UmaskKey, CalculateUmask(mask));
  238. return result;
  239. }
  240. [RubyMethod("umask", RubyMethodAttributes.PublicSingleton)]
  241. public static int GetUmask(RubyClass/*!*/ self) {
  242. return (int)self.Context.GetOrCreateLibraryData(UmaskKey, () => 0);
  243. }
  244. private static int CalculateUmask(int mask) {
  245. return (mask % 512) / 128 * 128;
  246. }
  247. #endregion
  248. #region delete, unlink, truncate, rename
  249. [RubyMethod("delete", RubyMethodAttributes.PublicSingleton)]
  250. [RubyMethod("unlink", RubyMethodAttributes.PublicSingleton)]
  251. public static int Delete(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path) {
  252. string strPath = self.Context.DecodePath(Protocols.CastToPath(toPath, path));
  253. if (!self.Context.Platform.FileExists(strPath)) {
  254. throw RubyExceptions.CreateENOENT("No such file or directory - {0}", strPath);
  255. }
  256. Delete(self.Context, strPath);
  257. return 1;
  258. }
  259. internal static void Delete(RubyContext/*!*/ context, string/*!*/ path) {
  260. try {
  261. context.Platform.DeleteFile(path, true);
  262. #if FEATURE_FILESYSTEM
  263. } catch (DirectoryNotFoundException) {
  264. throw RubyExceptions.CreateENOENT("No such file or directory - {0}", path);
  265. #endif
  266. } catch (IOException e) {
  267. throw Errno.CreateEACCES(e.Message, e);
  268. }
  269. }
  270. [RubyMethod("delete", RubyMethodAttributes.PublicSingleton)]
  271. [RubyMethod("unlink", RubyMethodAttributes.PublicSingleton)]
  272. public static int Delete(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, params object[] paths) {
  273. foreach (MutableString path in paths) {
  274. Delete(toPath, self, path);
  275. }
  276. return paths.Length;
  277. }
  278. #if FEATURE_FILESYSTEM
  279. [RubyMethod("truncate", BuildConfig = "FEATURE_FILESYSTEM")]
  280. public static int Truncate(RubyFile/*!*/ self, [DefaultProtocol]int size) {
  281. if (size < 0) {
  282. throw new InvalidError();
  283. }
  284. self.Length = size;
  285. return 0;
  286. }
  287. [RubyMethod("truncate", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  288. public static int Truncate(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path, [DefaultProtocol]int size) {
  289. if (size < 0) {
  290. throw new InvalidError();
  291. }
  292. using (RubyFile f = new RubyFile(self.Context, self.Context.DecodePath(Protocols.CastToPath(toPath, path)), IOMode.ReadWrite)) {
  293. f.Length = size;
  294. }
  295. return 0;
  296. }
  297. #endif
  298. [RubyMethod("rename", RubyMethodAttributes.PublicSingleton)]
  299. public static int Rename(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object oldPath, object newPath) {
  300. var context = self.Context;
  301. string strOldPath = context.DecodePath(Protocols.CastToPath(toPath, oldPath));
  302. string strNewPath = context.DecodePath(Protocols.CastToPath(toPath, newPath));
  303. if (strOldPath.Length == 0 || strNewPath.Length == 0) {
  304. throw RubyExceptions.CreateENOENT();
  305. }
  306. if (!context.Platform.FileExists(strOldPath) && !context.Platform.DirectoryExists(strOldPath)) {
  307. throw RubyExceptions.CreateENOENT("No such file or directory - {0}", oldPath);
  308. }
  309. if (RubyUtils.ExpandPath(context.Platform, strOldPath) == RubyUtils.ExpandPath(context.Platform, strNewPath)) {
  310. return 0;
  311. }
  312. if (context.Platform.FileExists(strNewPath)) {
  313. Delete(context, strNewPath);
  314. }
  315. try {
  316. context.Platform.MoveFileSystemEntry(strOldPath, strNewPath);
  317. } catch (IOException e) {
  318. throw Errno.CreateEACCES(e.Message, e);
  319. }
  320. return 0;
  321. }
  322. #endregion
  323. #region path, basename, dirname, extname, expand_path, absolute_path, fnmatch
  324. [RubyMethod("path", RubyMethodAttributes.PublicSingleton)]
  325. public static MutableString/*!*/ ToPath(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path) {
  326. return Protocols.CastToPath(toPath, path);
  327. }
  328. [RubyMethod("basename", RubyMethodAttributes.PublicSingleton)]
  329. public static MutableString/*!*/ BaseName(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self,
  330. object path, [DefaultProtocol, NotNull, Optional]MutableString suffix) {
  331. return BaseName(Protocols.CastToPath(toPath, path), suffix);
  332. }
  333. private static MutableString/*!*/ BaseName(MutableString/*!*/ path, MutableString suffix) {
  334. if (path.IsEmpty) {
  335. return path;
  336. }
  337. string strPath = path.ConvertToString();
  338. string[] parts = strPath.Split(new[] { DirectorySeparatorChar, AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
  339. if (parts.Length == 0) {
  340. return MutableString.CreateMutable(path.Encoding).Append((char)path.GetLastChar()).TaintBy(path);
  341. }
  342. #if WIN8 || WP75
  343. bool isWindows = true;
  344. #else
  345. bool isWindows = Environment.OSVersion.Platform != PlatformID.Unix && Environment.OSVersion.Platform != PlatformID.MacOSX;
  346. #endif
  347. if (isWindows) {
  348. string first = parts[0];
  349. if (strPath.Length >= 2 && IsDirectorySeparator(strPath[0]) && IsDirectorySeparator(strPath[1])) {
  350. // UNC: skip 2 parts
  351. if (parts.Length <= 2) {
  352. return MutableString.CreateMutable(path.Encoding).Append(DirectorySeparatorChar).TaintBy(path);
  353. }
  354. } else if (first.Length == 2 && Tokenizer.IsLetter(first[0]) && first[1] == ':') {
  355. // skip drive letter "X:"
  356. if (parts.Length <= 1) {
  357. var result = MutableString.CreateMutable(path.Encoding).TaintBy(path);
  358. if (strPath.Length > 2) {
  359. result.Append(strPath[2]);
  360. }
  361. return result;
  362. }
  363. }
  364. }
  365. string last = parts[parts.Length - 1];
  366. if (MutableString.IsNullOrEmpty(suffix)) {
  367. return MutableString.CreateMutable(last, path.Encoding);
  368. }
  369. #if WIN8
  370. StringComparison comparison = StringComparison.OrdinalIgnoreCase;
  371. #else
  372. StringComparison comparison = Environment.OSVersion.Platform == PlatformID.Unix ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
  373. #endif
  374. int matchLength = last.Length;
  375. if (suffix != null) {
  376. string strSuffix = suffix.ToString();
  377. if (strSuffix.LastCharacter() == '*' && strSuffix.Length > 1) {
  378. int suffixIdx = last.LastIndexOf(
  379. strSuffix.Substring(0, strSuffix.Length - 1),
  380. comparison
  381. );
  382. if (suffixIdx >= 0 && suffixIdx + strSuffix.Length <= last.Length) {
  383. matchLength = suffixIdx;
  384. }
  385. } else if (last.EndsWith(strSuffix, comparison)) {
  386. matchLength = last.Length - strSuffix.Length;
  387. }
  388. }
  389. return MutableString.CreateMutable(path.Encoding).Append(last, 0, matchLength).TaintBy(path);
  390. }
  391. [RubyMethod("dirname", RubyMethodAttributes.PublicSingleton)]
  392. public static MutableString/*!*/ DirName(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path) {
  393. return DirName(Protocols.CastToPath(toPath, path));
  394. }
  395. private static MutableString/*!*/ DirName(MutableString/*!*/ path) {
  396. string strPath = path.ConvertToString();
  397. string directoryName = strPath;
  398. if (IsValidPath(strPath)) {
  399. strPath = StripPathCharacters(strPath);
  400. // handle top-level UNC paths
  401. directoryName = Path.GetDirectoryName(strPath);
  402. if (directoryName == null) {
  403. return MutableString.CreateMutable(strPath, path.Encoding);
  404. }
  405. string fileName = Path.GetFileName(strPath);
  406. if (!String.IsNullOrEmpty(fileName)) {
  407. directoryName = StripPathCharacters(strPath.Substring(0, strPath.LastIndexOf(fileName, StringComparison.Ordinal)));
  408. }
  409. } else {
  410. if (directoryName.Length > 1) {
  411. directoryName = "//";
  412. }
  413. }
  414. directoryName = String.IsNullOrEmpty(directoryName) ? "." : directoryName;
  415. return MutableString.CreateMutable(directoryName, path.Encoding);
  416. }
  417. private static bool IsValidPath(string path) {
  418. foreach (char c in path) {
  419. if (c != '/' && c != '\\') {
  420. return true;
  421. }
  422. }
  423. return false;
  424. }
  425. private static string StripPathCharacters(string path) {
  426. int limit = 0;
  427. for (int charIndex = path.Length - 1; charIndex > 0; charIndex--) {
  428. if (!((path[charIndex] == '/') || (path[charIndex] == '\\')))
  429. break;
  430. limit++;
  431. }
  432. if (limit > 0) {
  433. limit--;
  434. if (path.Length == 3 && path[1] == ':') limit--;
  435. return path.Substring(0, path.Length - limit - 1);
  436. }
  437. return path;
  438. }
  439. [RubyMethod("extname", RubyMethodAttributes.PublicSingleton)]
  440. public static MutableString/*!*/ GetExtension(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path) {
  441. MutableString pathStr = Protocols.CastToPath(toPath, path);
  442. return MutableString.Create(RubyUtils.GetExtension(pathStr.ConvertToString()), pathStr.Encoding).TaintBy(pathStr);
  443. }
  444. [RubyMethod("expand_path", RubyMethodAttributes.PublicSingleton)]
  445. public static MutableString/*!*/ ExpandPath(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path,
  446. [DefaultParameterValue(null)]object basePath) {
  447. var context = self.Context;
  448. string result = RubyUtils.ExpandPath(
  449. context.Platform,
  450. context.DecodePath(Protocols.CastToPath(toPath, path)),
  451. (basePath == null) ? context.Platform.CurrentDirectory : context.DecodePath(Protocols.CastToPath(toPath, basePath)),
  452. true
  453. );
  454. return self.Context.EncodePath(result);
  455. }
  456. [RubyMethod("absolute_path", RubyMethodAttributes.PublicSingleton)]
  457. public static MutableString/*!*/ AbsolutePath(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path,
  458. [DefaultParameterValue(null)]object basePath) {
  459. var context = self.Context;
  460. string result = RubyUtils.ExpandPath(
  461. context.Platform,
  462. context.DecodePath(Protocols.CastToPath(toPath, path)),
  463. (basePath == null) ? context.Platform.CurrentDirectory : context.DecodePath(Protocols.CastToPath(toPath, basePath)),
  464. false
  465. );
  466. return self.Context.EncodePath(result);
  467. }
  468. [RubyMethod("fnmatch", RubyMethodAttributes.PublicSingleton)]
  469. [RubyMethod("fnmatch?", RubyMethodAttributes.PublicSingleton)]
  470. public static bool FnMatch(ConversionStorage<MutableString>/*!*/ toPath, object/*!*/ self,
  471. [DefaultProtocol, NotNull]MutableString/*!*/ pattern, object path, [Optional]int flags) {
  472. return Glob.FnMatch(pattern.ConvertToString(), Protocols.CastToPath(toPath, path).ConvertToString(), flags);
  473. }
  474. #endregion
  475. #region split, join
  476. [RubyMethod("split", RubyMethodAttributes.PublicSingleton)]
  477. public static RubyArray Split(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path) {
  478. MutableString p = Protocols.CastToPath(toPath, path);
  479. RubyArray result = new RubyArray(2);
  480. result.Add(DirName(p));
  481. result.Add(BaseName(p, null));
  482. return result;
  483. }
  484. [RubyMethod("join", RubyMethodAttributes.PublicSingleton)]
  485. public static MutableString Join(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, params object[]/*!*/ parts) {
  486. MutableString result = MutableString.CreateMutable(RubyEncoding.Binary);
  487. Dictionary<object, bool> visitedLists = null;
  488. var worklist = new Stack<object>();
  489. int current = 0;
  490. MutableString str;
  491. Push(worklist, parts);
  492. while (worklist.Count > 0) {
  493. object part = worklist.Pop();
  494. var list = part as IList;
  495. if (list != null) {
  496. if (list.Count == 0) {
  497. str = MutableString.FrozenEmpty;
  498. } else if (visitedLists != null && visitedLists.ContainsKey(list)) {
  499. str = RubyUtils.InfiniteRecursionMarker;
  500. } else {
  501. if (visitedLists == null) {
  502. visitedLists = new Dictionary<object, bool>(ReferenceEqualityComparer<object>.Instance);
  503. }
  504. visitedLists.Add(list, true);
  505. Push(worklist, list);
  506. continue;
  507. }
  508. } else if (part == null) {
  509. throw RubyExceptions.CreateTypeConversionError("NilClass", "String");
  510. } else {
  511. str = Protocols.CastToPath(toPath, part);
  512. }
  513. if (current > 0) {
  514. AppendDirectoryName(result, str);
  515. } else {
  516. result.Append(str);
  517. }
  518. current++;
  519. }
  520. return result;
  521. }
  522. private static void Push(Stack<Object>/*!*/ stack, IList/*!*/ values) {
  523. for (int i = values.Count - 1; i >= 0; i--) {
  524. stack.Push(values[i]);
  525. }
  526. }
  527. private static void AppendDirectoryName(MutableString/*!*/ result, MutableString/*!*/ name) {
  528. int resultLength = result.GetCharCount();
  529. int i;
  530. for (i = resultLength - 1; i >= 0; i--) {
  531. if (!IsDirectorySeparator(result.GetChar(i))) {
  532. break;
  533. }
  534. }
  535. if (i == resultLength - 1) {
  536. if (!IsDirectorySeparator(name.GetFirstChar())) {
  537. result.Append(DirectorySeparatorChar);
  538. }
  539. result.Append(name);
  540. } else if (IsDirectorySeparator(name.GetFirstChar())) {
  541. result.Replace(i + 1, resultLength - i - 1, name);
  542. } else {
  543. result.Append(name);
  544. }
  545. }
  546. #endregion
  547. #region flock, readlink, link, symlink
  548. #if FEATURE_FILESYSTEM
  549. //flock
  550. [RubyMethod("readlink", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  551. public static bool Readlink(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path) {
  552. throw new IronRuby.Builtins.NotImplementedError("readlink() function is unimplemented on this machine");
  553. }
  554. [RubyMethod("link", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  555. public static int Link(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object oldPath, object newPath) {
  556. Protocols.CastToPath(toPath, oldPath);
  557. Protocols.CastToPath(toPath, newPath);
  558. throw new IronRuby.Builtins.NotImplementedError("link not implemented");
  559. }
  560. [RubyMethod("symlink", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  561. public static object SymLink(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path) {
  562. throw new NotImplementedError("symlnk() function is unimplemented on this machine");
  563. }
  564. #endif
  565. #endregion
  566. #region atime, ctime, mtime, utime
  567. #if FEATURE_FILESYSTEM
  568. [RubyMethod("atime", BuildConfig = "FEATURE_FILESYSTEM")]
  569. public static RubyTime AccessTime(RubyContext/*!*/ context, RubyFile/*!*/ self) {
  570. return RubyStatOps.AccessTime(RubyStatOps.Create(self));
  571. }
  572. [RubyMethod("atime", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  573. public static RubyTime AccessTime(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path) {
  574. return RubyStatOps.AccessTime(RubyStatOps.Create(self.Context, Protocols.CastToPath(toPath, path)));
  575. }
  576. [RubyMethod("ctime", BuildConfig = "FEATURE_FILESYSTEM")]
  577. public static RubyTime CreateTime(RubyContext/*!*/ context, RubyFile/*!*/ self) {
  578. return RubyStatOps.CreateTime(RubyStatOps.Create(self));
  579. }
  580. [RubyMethod("ctime", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  581. public static RubyTime CreateTime(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path) {
  582. return RubyStatOps.CreateTime(RubyStatOps.Create(self.Context, Protocols.CastToPath(toPath, path)));
  583. }
  584. [RubyMethod("mtime", BuildConfig = "FEATURE_FILESYSTEM")]
  585. public static RubyTime ModifiedTime(RubyContext/*!*/ context, RubyFile/*!*/ self) {
  586. return RubyStatOps.ModifiedTime(RubyStatOps.Create(self));
  587. }
  588. [RubyMethod("mtime", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  589. public static RubyTime ModifiedTime(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path) {
  590. return RubyStatOps.ModifiedTime(RubyStatOps.Create(self.Context, Protocols.CastToPath(toPath, path)));
  591. }
  592. [RubyMethod("utime", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  593. public static int UpdateTimes(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, [NotNull]RubyTime/*!*/ accessTime, [NotNull]RubyTime/*!*/ modifiedTime,
  594. object path) {
  595. string strPath = self.Context.DecodePath(Protocols.CastToPath(toPath, path));
  596. FileInfo info = new FileInfo(strPath);
  597. if (!info.Exists) {
  598. throw RubyExceptions.CreateENOENT("No such file or directory - {0}", strPath);
  599. }
  600. info.LastAccessTimeUtc = accessTime.ToUniversalTime();
  601. info.LastWriteTimeUtc = modifiedTime.ToUniversalTime();
  602. return 1;
  603. }
  604. [RubyMethod("utime", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  605. public static int UpdateTimes(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object accessTime, object modifiedTime,
  606. params object[]/*!*/ paths) {
  607. RubyTime atime = MakeTime(self.Context, accessTime);
  608. RubyTime mtime = MakeTime(self.Context, modifiedTime);
  609. foreach (MutableString path in paths) {
  610. UpdateTimes(toPath, self, atime, mtime, path);
  611. }
  612. return paths.Length;
  613. }
  614. #endif
  615. private static RubyTime MakeTime(RubyContext/*!*/ context, object obj) {
  616. if (obj == null) {
  617. return new RubyTime(DateTime.Now);
  618. } else if (obj is RubyTime) {
  619. return (RubyTime)obj;
  620. } else if (obj is int) {
  621. return new RubyTime(RubyTime.Epoch.AddSeconds((int)obj));
  622. } else if (obj is double) {
  623. return new RubyTime(RubyTime.Epoch.AddSeconds((double)obj));
  624. } else {
  625. string name = context.GetClassOf(obj).Name;
  626. throw RubyExceptions.CreateTypeConversionError(name, "time");
  627. }
  628. }
  629. #endregion
  630. #region ftype, stat, inspect, path, to_path
  631. #if FEATURE_FILESYSTEM
  632. [RubyMethod("ftype", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  633. public static MutableString FileType(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path) {
  634. return RubyStatOps.FileType(RubyStatOps.Create(self.Context, Protocols.CastToPath(toPath, path)));
  635. }
  636. [RubyMethod("lstat", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  637. [RubyMethod("stat", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  638. public static FileSystemInfo/*!*/ Stat(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path) {
  639. return RubyStatOps.Create(self.Context, Protocols.CastToPath(toPath, path));
  640. }
  641. [RubyMethod("lstat", BuildConfig = "FEATURE_FILESYSTEM")]
  642. [RubyMethod("stat", BuildConfig = "FEATURE_FILESYSTEM")]
  643. public static FileSystemInfo Stat(RubyFile/*!*/ self) {
  644. return RubyStatOps.Create(self);
  645. }
  646. #endif
  647. [RubyMethod("inspect")]
  648. public static MutableString/*!*/ Inspect(RubyFile/*!*/ self) {
  649. return MutableString.CreateMutable(self.Context.GetPathEncoding()).
  650. Append("#<").
  651. Append(self.Context.GetClassOf(self).GetName(self.Context)).
  652. Append(':').
  653. Append(self.Path).
  654. Append(self.Closed ? " (closed)" : "").
  655. Append('>');
  656. }
  657. [RubyMethod("path")]
  658. [RubyMethod("to_path")]
  659. public static MutableString GetPath(RubyFile/*!*/ self) {
  660. self.RequireInitialized();
  661. return self.Path != null ? self.Context.EncodePath(self.Path) : null;
  662. }
  663. #endregion
  664. #region File::Stat
  665. #if FEATURE_FILESYSTEM
  666. /// <summary>
  667. /// Stat
  668. /// </summary>
  669. [RubyClass("Stat", Extends = typeof(FileSystemInfo), Inherits = typeof(object), BuildConfig = "FEATURE_FILESYSTEM"), Includes(typeof(Comparable))]
  670. public class RubyStatOps {
  671. // TODO: should work for IO and files w/o paths:
  672. internal static FileSystemInfo/*!*/ Create(RubyFile/*!*/ file) {
  673. file.RequireInitialized();
  674. if (file.Path == null) {
  675. throw new NotSupportedException("TODO: cannot get file info for files without path");
  676. }
  677. return Create(file.Context, file.Path);
  678. }
  679. internal static FileSystemInfo/*!*/ Create(RubyContext/*!*/ context, MutableString/*!*/ path) {
  680. return Create(context, context.DecodePath(path));
  681. }
  682. internal static FileSystemInfo/*!*/ Create(RubyContext/*!*/ context, string/*!*/ path) {
  683. FileSystemInfo fsi;
  684. if (TryCreate(context, path, out fsi)) {
  685. return fsi;
  686. } else {
  687. throw RubyExceptions.CreateENOENT("No such file or directory - {0}", path);
  688. }
  689. }
  690. internal static bool TryCreate(RubyContext/*!*/ context, string/*!*/ path, out FileSystemInfo result) {
  691. PlatformAdaptationLayer pal = context.Platform;
  692. result = null;
  693. if (pal.FileExists(path)) {
  694. result = new FileInfo(path);
  695. } else if (pal.DirectoryExists(path)) {
  696. result = new DirectoryInfo(path);
  697. } else if (path.ToUpperInvariant().Equals(NUL_VALUE)) {
  698. result = new DeviceInfo(NUL_VALUE);
  699. } else {
  700. return false;
  701. }
  702. return true;
  703. }
  704. [RubyConstructor]
  705. public static FileSystemInfo/*!*/ Create(ConversionStorage<MutableString>/*!*/ toPath, RubyClass/*!*/ self, object path) {
  706. return Create(self.Context, Protocols.CastToPath(toPath, path));
  707. }
  708. [RubyMethod("<=>")]
  709. public static int Compare(FileSystemInfo/*!*/ self, [NotNull]FileSystemInfo/*!*/ other) {
  710. return self.LastWriteTime.CompareTo(other.LastWriteTime);
  711. }
  712. [RubyMethod("<=>")]
  713. public static object Compare(FileSystemInfo/*!*/ self, object other) {
  714. Debug.Assert(other as FileSystemInfo == null);
  715. return null;
  716. }
  717. [RubyMethod("atime")]
  718. public static RubyTime/*!*/ AccessTime(FileSystemInfo/*!*/ self) {
  719. return new RubyTime(self.LastAccessTime);
  720. }
  721. [RubyMethod("blksize")]
  722. public static object BlockSize(FileSystemInfo/*!*/ self) {
  723. return null;
  724. }
  725. [RubyMethod("blockdev?")]
  726. public static bool IsBlockDevice(FileSystemInfo/*!*/ self) {
  727. return false;
  728. }
  729. [RubyMethod("blocks")]
  730. public static object Blocks(FileSystemInfo/*!*/ self) {
  731. return null;
  732. }
  733. [RubyMethod("chardev?")]
  734. public static bool IsCharDevice(FileSystemInfo/*!*/ self) {
  735. return false;
  736. }
  737. [RubyMethod("ctime")]
  738. public static RubyTime/*!*/ CreateTime(FileSystemInfo/*!*/ self) {
  739. return new RubyTime(self.CreationTime);
  740. }
  741. [RubyMethod("dev")]
  742. [RubyMethod("rdev")]
  743. public static object DeviceId(FileSystemInfo/*!*/ self) {
  744. // TODO: Map to drive letter?
  745. return 3;
  746. }
  747. [RubyMethod("dev_major")]
  748. [RubyMethod("rdev_major")]
  749. public static object DeviceIdMajor(FileSystemInfo/*!*/ self) {
  750. return null;
  751. }
  752. [RubyMethod("dev_minor")]
  753. [RubyMethod("rdev_minor")]
  754. public static object DeviceIdMinor(FileSystemInfo/*!*/ self) {
  755. return null;
  756. }
  757. [RubyMethod("directory?")]
  758. public static bool IsDirectory(FileSystemInfo/*!*/ self) {
  759. return (self is DirectoryInfo);
  760. }
  761. [RubyMethod("executable?")]
  762. [RubyMethod("executable_real?")]
  763. public static bool IsExecutable(FileSystemInfo/*!*/ self) {
  764. // TODO: Fix
  765. return self.Extension.Equals(".exe", StringComparison.OrdinalIgnoreCase);
  766. }
  767. [RubyMethod("identical?")]
  768. public static bool AreIdentical(RubyContext/*!*/ context, FileSystemInfo/*!*/ self, [NotNull]FileSystemInfo/*!*/ other) {
  769. // TODO: links
  770. return self.Exists && other.Exists && context.Platform.PathComparer.Compare(self.FullName, other.FullName) == 0;
  771. }
  772. [RubyMethod("file?")]
  773. public static bool IsFile(FileSystemInfo/*!*/ self) {
  774. return self is FileInfo;
  775. }
  776. [RubyMethod("ftype")]
  777. public static MutableString FileType(FileSystemInfo/*!*/ self) {
  778. return MutableString.CreateAscii(IsFile(self) ? "file" : "directory");
  779. }
  780. [RubyMethod("gid")]
  781. public static int GroupId(FileSystemInfo/*!*/ self) {
  782. return 0;
  783. }
  784. [RubyMethod("grpowned?")]
  785. public static bool IsGroupOwned(FileSystemInfo/*!*/ self) {
  786. return false;
  787. }
  788. [RubyMethod("ino")]
  789. public static int Inode(FileSystemInfo/*!*/ self) {
  790. return 0;
  791. }
  792. [RubyMethod("inspect")]
  793. public static MutableString/*!*/ Inspect(RubyContext/*!*/ context, FileSystemInfo/*!*/ self) {
  794. return MutableString.CreateAscii(String.Format(CultureInfo.InvariantCulture,
  795. "#<File::Stat dev={0}, ino={1}, mode={2}, nlink={3}, uid={4}, gid={5}, rdev={6}, size={7}, blksize={8}, blocks={9}, atime={10}, mtime={11}, ctime={12}",
  796. context.Inspect(DeviceId(self)),
  797. context.Inspect(Inode(self)),
  798. context.Inspect(Mode(self)),
  799. context.Inspect(NumberOfLinks(self)),
  800. context.Inspect(UserId(self)),
  801. context.Inspect(GroupId(self)),
  802. context.Inspect(DeviceId(self)),
  803. context.Inspect(Size(self)),
  804. context.Inspect(BlockSize(self)),
  805. context.Inspect(Blocks(self)),
  806. context.Inspect(AccessTime(self)),
  807. context.Inspect(ModifiedTime(self)),
  808. context.Inspect(CreateTime(self))
  809. ));
  810. }
  811. [RubyMethod("mode")]
  812. public static int Mode(FileSystemInfo/*!*/ self) {
  813. int mode = (self is FileInfo) ? 0x8000 : 0x4000;
  814. mode |= 0x100; // S_IREAD;
  815. if ((self.Attributes & FileAttributes.ReadOnly) == 0) {
  816. mode |= 0x80; // S_IWRITE;
  817. }
  818. return mode;
  819. }
  820. [RubyMethod("mtime")]
  821. public static RubyTime/*!*/ ModifiedTime(FileSystemInfo/*!*/ self) {
  822. return new RubyTime(self.LastWriteTime);
  823. }
  824. [RubyMethod("nlink")]
  825. public static int NumberOfLinks(FileSystemInfo/*!*/ self) {
  826. return 1;
  827. }
  828. [RubyMethod("owned?")]
  829. public static bool IsUserOwned(FileSystemInfo/*!*/ self) {
  830. return true;
  831. }
  832. [RubyMethod("pipe?")]
  833. public static bool IsPipe(FileSystemInfo/*!*/ self) {
  834. return false;
  835. }
  836. [RubyMethod("readable?")]
  837. [RubyMethod("readable_real?")]
  838. public static bool IsReadable(FileSystemInfo/*!*/ self) {
  839. // TODO: Security, including identifying that we're impersonating another principal
  840. // ie. System.Security.AccessControl control = info.GetAccessControl();
  841. return true;
  842. }
  843. [RubyMethod("setgid?")]
  844. public static bool IsSetGid(FileSystemInfo/*!*/ self) {
  845. return false;
  846. }
  847. [RubyMethod("setuid?")]
  848. public static bool IsSetUid(FileSystemInfo/*!*/ self) {
  849. return false;
  850. }
  851. [RubyMethod("size")]
  852. public static int Size(FileSystemInfo/*!*/ self) {
  853. if (self is DeviceInfo) {
  854. return 0;
  855. }
  856. FileInfo info = (self as FileInfo);
  857. return (info == null) ? 0 : (int)info.Length;
  858. }
  859. [RubyMethod("size?")]
  860. public static object NullableSize(FileSystemInfo/*!*/ self) {
  861. if (self is DeviceInfo) {
  862. return 0;
  863. }
  864. FileInfo info = (self as FileInfo);
  865. if (info == null) {
  866. return null;
  867. }
  868. return (info.Length == 0) ? null : (object)(int)info.Length;
  869. }
  870. [RubyMethod("socket?")]
  871. public static bool IsSocket(FileSystemInfo/*!*/ self) {
  872. return false;
  873. }
  874. [RubyMethod("sticky?")]
  875. public static object IsSticky(FileSystemInfo/*!*/ self) {
  876. return null;
  877. }
  878. [RubyMethod("symlink?")]
  879. public static bool IsSymLink(FileSystemInfo/*!*/ self) {
  880. return false;
  881. }
  882. [RubyMethod("uid")]
  883. public static int UserId(FileSystemInfo/*!*/ self) {
  884. return 0;
  885. }
  886. [RubyMethod("writable?")]
  887. [RubyMethod("writable_real?")]
  888. public static bool IsWritable(FileSystemInfo/*!*/ self) {
  889. // TODO: Security, including identifying that we're impersonating another principal
  890. // ie. System.Security.AccessControl control = info.GetAccessControl();
  891. return ((self.Attributes & FileAttributes.ReadOnly) == 0);
  892. }
  893. [RubyMethod("zero?")]
  894. public static bool IsZeroLength(FileSystemInfo/*!*/ self) {
  895. if (self is DeviceInfo) {
  896. return true;
  897. }
  898. FileInfo info = (self as FileInfo);
  899. return (info == null) ? false : info.Length == 0;
  900. }
  901. // cannot inherit from FileSystemInfo in Silverlight because the
  902. // constructor is SecurityCritical
  903. internal class DeviceInfo : FileSystemInfo {
  904. private string/*!*/ _name;
  905. internal DeviceInfo(string/*!*/ name) {
  906. _name = name;
  907. }
  908. public override void Delete() {
  909. throw new NotImplementedException();
  910. }
  911. public override bool Exists {
  912. get { return true; }
  913. }
  914. public override string Name {
  915. get { return _name; }
  916. }
  917. }
  918. }
  919. #endif
  920. #endregion
  921. #region FileTest methods (public singletons only)
  922. #if FEATURE_FILESYSTEM
  923. // TODO: conversion: to_io, to_path, to_str
  924. [RubyMethod("blockdev?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  925. public static bool IsBlockDevice(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path) {
  926. return FileTest.IsBlockDevice(toPath, self, path);
  927. }
  928. [RubyMethod("chardev?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  929. public static bool IsCharDevice(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path) {
  930. return FileTest.IsCharDevice(toPath, self, path);
  931. }
  932. [RubyMethod("directory?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  933. public static bool IsDirectory(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path) {
  934. return FileTest.IsDirectory(toPath, self, path);
  935. }
  936. [RubyMethod("executable?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  937. [RubyMethod("executable_real?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  938. public static bool IsExecutable(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path) {
  939. return FileTest.IsExecutable(toPath, self, path);
  940. }
  941. [RubyMethod("exist?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  942. [RubyMethod("exists?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  943. public static bool Exists(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path) {
  944. return FileTest.Exists(toPath, self, path);
  945. }
  946. [RubyMethod("file?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  947. public static bool IsFile(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path) {
  948. return FileTest.IsFile(toPath, self, path);
  949. }
  950. [RubyMethod("grpowned?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  951. public static bool IsGroupOwned(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path) {
  952. return FileTest.IsGroupOwned(toPath, self, path);
  953. }
  954. [RubyMethod("identical?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  955. public static bool AreIdentical(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path1, object path2) {
  956. return FileTest.AreIdentical(toPath, self, path1, path2);
  957. }
  958. [RubyMethod("owned?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  959. public static bool IsUserOwned(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path) {
  960. return FileTest.IsUserOwned(toPath, self, path);
  961. }
  962. [RubyMethod("pipe?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  963. public static bool IsPipe(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path) {
  964. return FileTest.IsPipe(toPath, self, path);
  965. }
  966. [RubyMethod("readable?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  967. [RubyMethod("readable_real?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  968. public static bool IsReadable(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path) {
  969. return FileTest.IsReadable(toPath, self, path);
  970. }
  971. [RubyMethod("setgid?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  972. public static bool IsSetGid(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path) {
  973. return FileTest.IsSetGid(toPath, self, path);
  974. }
  975. [RubyMethod("setuid?", RubyMethodAttributes.PublicSingleton, BuildConfig = "FEATURE_FILESYSTEM")]
  976. public static bool IsSetUid(ConversionStorage<MutableString>/*!*/ toPath, RubyModule/*!*/ self, object path) {
  977. ret

Large files files are truncated, but you can click here to view the full file