PageRenderTime 59ms CodeModel.GetById 3ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_2_0/Src/IronPython.Modules/nt.cs

#
C# | 1222 lines | 957 code | 203 blank | 62 comment | 162 complexity | e7ee776990e832a75b198c4a05b1ec77 MD5 | raw file
   1/* ****************************************************************************
   2 *
   3 * Copyright (c) Microsoft Corporation. 
   4 *
   5 * This source code is subject to terms and conditions of the Microsoft Public License. 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  Microsoft Public License, please send an email to 
   8 * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
   9 * by the terms of the Microsoft Public License.
  10 *
  11 * You must not remove this notice, or any other, from this software.
  12 *
  13 *
  14 * ***************************************************************************/
  15
  16
  17using System; using Microsoft;
  18using System.Collections;
  19using System.Collections.Generic;
  20using System.ComponentModel;
  21using System.Diagnostics;
  22using System.Globalization;
  23using System.IO;
  24using System.Runtime.InteropServices;
  25using System.Security.Cryptography;
  26
  27using Microsoft.Scripting;
  28using Microsoft.Scripting.Math;
  29using Microsoft.Scripting.Runtime;
  30using Microsoft.Scripting.Utils;
  31
  32using IronPython.Runtime;
  33using IronPython.Runtime.Exceptions;
  34using IronPython.Runtime.Operations;
  35using IronPython.Runtime.Types;
  36
  37[assembly: PythonModule("nt", typeof(IronPython.Modules.PythonNT))]
  38namespace IronPython.Modules {
  39    public static class PythonNT {
  40        #region Public API Surface
  41
  42#if !SILVERLIGHT // FailFast
  43        public static void abort() {
  44            System.Environment.FailFast("IronPython os.abort");
  45        }
  46#endif
  47
  48        /// <summary>
  49        /// Checks for the specific permissions, provided by the mode parameter, are available for the provided path.  Permissions can be:
  50        /// 
  51        /// F_OK: Check to see if the file exists
  52        /// R_OK | W_OK | X_OK: Check for the specific permissions.  Only W_OK is respected.
  53        /// </summary>
  54        public static bool access(CodeContext/*!*/ context, string path, int mode) {
  55            if (path == null) throw PythonOps.TypeError("expected string, got None");
  56
  57            if (mode == F_OK) {
  58                return context.LanguageContext.DomainManager.Platform.FileExists(path);
  59            }
  60#if !SILVERLIGHT
  61            // match the behavior of the VC C Runtime
  62            FileAttributes fa = File.GetAttributes(path);
  63            if ((fa & FileAttributes.Directory) != 0) {
  64                // directories have read & write access
  65                return true;
  66            }
  67
  68            if ((fa & FileAttributes.ReadOnly) != 0 && (mode & W_OK) != 0) {
  69                // want to write but file is read-only
  70                return false;
  71            }
  72
  73            return true;
  74#else
  75            return false;
  76#endif
  77        }
  78
  79#if !SILVERLIGHT // SetCurrentDirectory, FileInfo
  80        public static void chdir([NotNull]string path) {
  81            if (String.IsNullOrEmpty(path)) {
  82                throw PythonExceptions.CreateThrowable(WindowsError, PythonErrorNumber.EINVAL, "Path cannot be an empty string");
  83            }
  84
  85            try {
  86                Directory.SetCurrentDirectory(path);
  87            } catch (Exception e) {
  88                throw ToPythonException(e);
  89            }
  90        }
  91
  92        public static void chmod(string path, int mode) {
  93            FileInfo fi = new FileInfo(path);
  94            if ((mode & S_IWRITE) != 0) {
  95                fi.Attributes &= ~(FileAttributes.ReadOnly);
  96            } else {
  97                fi.Attributes |= FileAttributes.ReadOnly;
  98            }
  99        }
 100#endif
 101
 102        public static void close(CodeContext/*!*/ context, int fd) {
 103            PythonContext pythonContext = PythonContext.GetContext(context);
 104            PythonFile pf = pythonContext.FileManager.GetFileFromId(pythonContext, fd);
 105            pf.close();
 106        }
 107        
 108#if !SILVERLIGHT
 109        /// <summary>
 110        /// single instance of environment dictionary is shared between multiple runtimes because the environment
 111        /// is shared by multiple runtimes.
 112        /// </summary>
 113        public static readonly object environ = new PythonDictionary(new EnvironmentDictionaryStorage());
 114#endif
 115
 116        public static readonly PythonType error = Builtin.OSError;
 117
 118        public static void _exit(CodeContext/*!*/ context, int code) {
 119            PythonContext.GetContext(context).DomainManager.Platform.TerminateScriptExecution(code);
 120        }
 121
 122        public static object fdopen(CodeContext/*!*/ context, int fd) {
 123            return fdopen(context, fd, "r");
 124        }
 125
 126        public static object fdopen(CodeContext/*!*/ context, int fd, string mode) {
 127            return fdopen(context, fd, mode, 0);
 128        }
 129
 130        public static object fdopen(CodeContext/*!*/ context, int fd, string mode, int bufsize) {
 131            // check for a valid file mode...
 132            PythonFile.ValidateMode(mode);
 133
 134            PythonContext pythonContext = PythonContext.GetContext(context);
 135            PythonFile pf = pythonContext.FileManager.GetFileFromId(pythonContext, fd);
 136            return pf;
 137        }
 138
 139        public static object fstat(CodeContext/*!*/ context, int fd) {
 140            PythonContext pythonContext = PythonContext.GetContext(context);
 141            PythonFile pf = pythonContext.FileManager.GetFileFromId(pythonContext, fd);
 142            if (pf.IsConsole) {
 143                return new stat_result(8192);
 144            }
 145            return lstat(pf.name);
 146        }
 147
 148        public static string getcwd(CodeContext/*!*/ context) {
 149            return context.LanguageContext.DomainManager.Platform.CurrentDirectory;
 150        }
 151
 152        public static string getcwdu(CodeContext/*!*/ context) {
 153            return context.LanguageContext.DomainManager.Platform.CurrentDirectory;
 154        }
 155
 156        public static string _getfullpathname(CodeContext/*!*/ context, [NotNull]string/*!*/ dir) {
 157            PlatformAdaptationLayer pal = context.LanguageContext.DomainManager.Platform;
 158
 159            try {
 160                return context.LanguageContext.DomainManager.Platform.GetFullPath(dir);
 161            } catch (ArgumentException) {
 162                // .NET validates the path, CPython doesn't... so we replace invalid chars with 
 163                // Char.Maxvalue, get the full path, and then replace the Char.Maxvalue's back w/ 
 164                // their original value.
 165                string newdir = dir;
 166                
 167                foreach (char c in Path.GetInvalidPathChars()) {
 168                    newdir = newdir.Replace(c, Char.MaxValue);
 169                }
 170
 171                // walk backwards through the path replacing the same characters.  We should have
 172                // only updated the directory leaving the filename which we're fixing.
 173                string res = context.LanguageContext.DomainManager.Platform.GetFullPath(newdir);
 174                int curDir = dir.Length;
 175                for (int curRes = res.Length - 1; curRes >= 0; curRes--) {
 176                    if (res[curRes] == Char.MaxValue) {
 177                        for (curDir--; curDir >= 0; curDir--) {
 178                            if (newdir[curDir] == Char.MaxValue) {
 179                                res = res.Substring(0, curRes) + dir[curDir] + res.Substring(curRes + 1);
 180                                break;
 181                            }
 182                        }
 183                    }
 184                }
 185
 186                return res;
 187            }
 188        }
 189
 190#if !SILVERLIGHT
 191        public static int getpid() {
 192            return System.Diagnostics.Process.GetCurrentProcess().Id;
 193        }
 194#endif
 195
 196        public static List listdir(CodeContext/*!*/ context, string path) {
 197            List ret = PythonOps.MakeList();
 198            try {
 199                string[] files = context.LanguageContext.DomainManager.Platform.GetFiles(path, "*");
 200                addBase(files, ret);
 201                addBase(context.LanguageContext.DomainManager.Platform.GetDirectories(path, "*"), ret);
 202                return ret;
 203            } catch (Exception e) {
 204                throw ToPythonException(e);
 205            }
 206        }
 207
 208        // 
 209        // lstat(path) -> stat result
 210        // Like stat(path), but do not follow symbolic links.
 211        // 
 212        public static object lstat(string path) {
 213            return stat(path);
 214        }
 215
 216#if !SILVERLIGHT
 217        public static void mkdir(string path) {
 218            if (Directory.Exists(path))
 219                throw DirectoryExists();
 220
 221            try {
 222                Directory.CreateDirectory(path);
 223            } catch (Exception e) {
 224                throw ToPythonException(e);
 225            }
 226        }
 227
 228        public static void mkdir(string path, int mode) {
 229            if (Directory.Exists(path)) throw DirectoryExists();
 230            // we ignore mode
 231
 232            try {
 233                Directory.CreateDirectory(path);
 234            } catch (Exception e) {
 235                throw ToPythonException(e);
 236            }
 237        }
 238#endif
 239
 240        public static object open(CodeContext/*!*/ context, string filename, int flag) {
 241            return open(context, filename, flag, 0777);
 242        }
 243
 244        public static object open(CodeContext/*!*/ context, string filename, int flag, int mode) {
 245            try {
 246                FileStream fs = File.Open(filename, FileModeFromFlags(flag), FileAccessFromFlags(flag), FileShare.ReadWrite);
 247
 248                string mode2;
 249                if (fs.CanRead && fs.CanWrite) mode2 = "w+";
 250                else if (fs.CanWrite) mode2 = "w";
 251                else mode2 = "r";
 252
 253                if ((flag & O_BINARY) != 0) {
 254                    mode2 += "b";
 255                }
 256
 257                return PythonContext.GetContext(context).FileManager.AddToStrongMapping(PythonFile.Create(context, fs, filename, mode2));
 258            } catch (Exception e) {
 259                throw ToPythonException(e);
 260            }
 261        }
 262
 263#if !SILVERLIGHT
 264        public static PythonFile popen(CodeContext/*!*/ context, string command) {
 265            return popen(context, command, "r");
 266        }
 267
 268        public static PythonFile popen(CodeContext/*!*/ context, string command, string mode) {
 269            return popen(context, command, mode, 4096);
 270        }
 271
 272        public static PythonFile popen(CodeContext/*!*/ context, string command, string mode, int bufsize) {
 273            if (String.IsNullOrEmpty(mode)) mode = "r";
 274            ProcessStartInfo psi = GetProcessInfo(command);
 275            psi.CreateNoWindow = true;  // ipyw shouldn't create a new console window
 276            Process p;
 277            PythonFile res;
 278
 279            try {
 280                switch (mode) {
 281                    case "r":
 282                        psi.RedirectStandardOutput = true;
 283                        p = Process.Start(psi);
 284
 285                        res = new POpenFile(context, command, p, p.StandardOutput.BaseStream, "r");
 286                        break;
 287                    case "w":
 288                        psi.RedirectStandardInput = true;
 289                        p = Process.Start(psi);
 290
 291                        res = new POpenFile(context, command, p, p.StandardInput.BaseStream, "w");
 292                        break;
 293                    default:
 294                        throw PythonOps.ValueError("expected 'r' or 'w' for mode, got {0}", mode);
 295                }
 296            } catch (Exception e) {
 297                throw ToPythonException(e);
 298            }
 299
 300            return res;
 301        }
 302
 303        public static PythonTuple popen2(CodeContext/*!*/ context, string command) {
 304            return popen2(context, command, "t");
 305        }
 306
 307        public static PythonTuple popen2(CodeContext/*!*/ context, string command, string mode) {
 308            return popen2(context, command, "t", 4096);
 309        }
 310
 311        public static PythonTuple popen2(CodeContext/*!*/ context, string command, string mode, int bufsize) {
 312            if (String.IsNullOrEmpty(mode)) mode = "t";
 313            if (mode != "t" && mode != "b") throw PythonOps.ValueError("mode must be 't' or 'b' (default is t)");
 314            if (mode == "t") mode = String.Empty;
 315
 316            try {
 317                ProcessStartInfo psi = GetProcessInfo(command);
 318                psi.RedirectStandardInput = true;
 319                psi.RedirectStandardOutput = true;
 320                psi.CreateNoWindow = true; // ipyw shouldn't create a new console window
 321                Process p = Process.Start(psi);
 322
 323                return PythonTuple.MakeTuple(new POpenFile(context, command, p, p.StandardInput.BaseStream, "w" + mode),
 324                        new POpenFile(context, command, p, p.StandardOutput.BaseStream, "r" + mode));
 325            } catch (Exception e) {
 326                throw ToPythonException(e);
 327            }
 328        }
 329
 330        public static PythonTuple popen3(CodeContext/*!*/ context, string command) {
 331            return popen3(context, command, "t");
 332        }
 333
 334        public static PythonTuple popen3(CodeContext/*!*/ context, string command, string mode) {
 335            return popen3(context, command, "t", 4096);
 336        }
 337
 338        public static PythonTuple popen3(CodeContext/*!*/ context, string command, string mode, int bufsize) {
 339            if (String.IsNullOrEmpty(mode)) mode = "t";
 340            if (mode != "t" && mode != "b") throw PythonOps.ValueError("mode must be 't' or 'b' (default is t)");
 341            if (mode == "t") mode = String.Empty;
 342
 343            try {
 344                ProcessStartInfo psi = GetProcessInfo(command);
 345                psi.RedirectStandardInput = true;
 346                psi.RedirectStandardOutput = true;
 347                psi.RedirectStandardError = true;
 348                psi.CreateNoWindow = true; // ipyw shouldn't create a new console window
 349                Process p = Process.Start(psi);
 350
 351                return PythonTuple.MakeTuple(new POpenFile(context, command, p, p.StandardInput.BaseStream, "w" + mode),
 352                        new POpenFile(context, command, p, p.StandardOutput.BaseStream, "r" + mode),
 353                        new POpenFile(context, command, p, p.StandardError.BaseStream, "r+" + mode));
 354            } catch (Exception e) {
 355                throw ToPythonException(e);
 356            }
 357        }
 358
 359        public static void putenv(string varname, string value) {
 360            try {
 361                System.Environment.SetEnvironmentVariable(varname, value);
 362            } catch (Exception e) {
 363                throw ToPythonException(e);
 364            }
 365        }
 366#endif
 367
 368        public static string read(CodeContext/*!*/ context, int fd, int buffersize) {
 369            if (buffersize < 0) {
 370                throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, PythonErrorNumber.EINVAL, "Invalid argument");
 371            }
 372
 373            try {
 374                PythonContext pythonContext = PythonContext.GetContext(context);
 375                PythonFile pf = pythonContext.FileManager.GetFileFromId(pythonContext, fd);
 376                return pf.read();
 377            } catch (Exception e) {
 378                throw ToPythonException(e);
 379            }
 380        }
 381
 382        public static void remove(string path) {
 383            UnlinkWorker(path);
 384        }
 385
 386        public static void rename(string src, string dst) {
 387            try {
 388                Directory.Move(src, dst);
 389            } catch (Exception e) {
 390                throw ToPythonException(e);
 391            }
 392        }
 393
 394        public static void rmdir(string path) {
 395            try {
 396                Directory.Delete(path);
 397            } catch (Exception e) {
 398                throw ToPythonException(e);
 399            }
 400        }
 401
 402#if !SILVERLIGHT
 403        public static object spawnl(CodeContext/*!*/ context, int mode, string path, params object[] args) {
 404            return SpawnProcessImpl(context, MakeProcess(), mode, path, args);
 405        }
 406
 407        public static object spawnle(CodeContext/*!*/ context, int mode, string path, params object[] args) {
 408            if (args.Length < 1) {
 409                throw PythonOps.TypeError("spawnle() takes at least three arguments ({0} given)", 2 + args.Length);
 410            }
 411
 412            object env = args[args.Length - 1];
 413            object[] slicedArgs = ArrayUtils.RemoveFirst(args);
 414
 415            Process process = MakeProcess();
 416            SetEnvironment(process.StartInfo.EnvironmentVariables, env);
 417
 418            return SpawnProcessImpl(context, process, mode, path, slicedArgs);
 419        }
 420
 421        public static object spawnv(CodeContext/*!*/ context, int mode, string path, object args) {
 422            return SpawnProcessImpl(context, MakeProcess(), mode, path, args);
 423        }
 424
 425        public static object spawnve(CodeContext/*!*/ context, int mode, string path, object args, object env) {
 426            Process process = MakeProcess();
 427            SetEnvironment(process.StartInfo.EnvironmentVariables, env);
 428
 429            return SpawnProcessImpl(context, process, mode, path, args);
 430        }
 431
 432        private static Process MakeProcess() {
 433            try {
 434                return new Process();
 435            } catch (Exception e) {
 436                throw ToPythonException(e);
 437            }
 438        }
 439
 440        private static object SpawnProcessImpl(CodeContext/*!*/ context, Process process, int mode, string path, object args) {
 441            try {
 442                process.StartInfo.Arguments = ArgumentsToString(context, args);
 443                process.StartInfo.FileName = path;
 444                process.StartInfo.UseShellExecute = false;
 445            } catch (Exception e) {
 446                throw ToPythonException(e);
 447            }
 448
 449            if (!process.Start()) {
 450                throw PythonOps.OSError("Cannot start process: {0}", path);
 451            }
 452            if (mode == (int)P_WAIT) {
 453                process.WaitForExit();
 454                int exitCode = process.ExitCode;
 455                process.Close();
 456                return exitCode;
 457            } else {
 458                return process.Id;
 459            }
 460        }
 461
 462        /// <summary>
 463        /// Copy elements from a Python mapping of dict environment variables to a StringDictionary.
 464        /// </summary>
 465        private static void SetEnvironment(System.Collections.Specialized.StringDictionary currentEnvironment, object newEnvironment) {
 466            PythonDictionary env = newEnvironment as PythonDictionary;
 467            if (env == null) {
 468                throw PythonOps.TypeError("env argument must be a dict");
 469            }
 470
 471            currentEnvironment.Clear();
 472
 473            string strKey, strValue;
 474            foreach (object key in env.keys()) {
 475                if (!Converter.TryConvertToString(key, out strKey)) {
 476                    throw PythonOps.TypeError("env dict contains a non-string key");
 477                }
 478                if (!Converter.TryConvertToString(env[key], out strValue)) {
 479                    throw PythonOps.TypeError("env dict contains a non-string value");
 480                }
 481                currentEnvironment[strKey] = strValue;
 482            }
 483        }
 484#endif
 485
 486        /// <summary>
 487        /// Convert a sequence of args to a string suitable for using to spawn a process.
 488        /// </summary>
 489        private static string ArgumentsToString(CodeContext/*!*/ context, object args) {
 490            IEnumerator argsEnumerator;
 491            System.Text.StringBuilder sb = null;
 492            if (!PythonOps.TryGetEnumerator(context, args, out argsEnumerator)) {
 493                throw PythonOps.TypeError("args parameter must be sequence, not {0}", DynamicHelpers.GetPythonType(args));
 494            }
 495
 496            bool space = false;
 497            try {
 498                // skip the first element, which is the name of the command being run
 499                argsEnumerator.MoveNext();
 500                while (argsEnumerator.MoveNext()) {
 501                    if (sb == null) sb = new System.Text.StringBuilder(); // lazy creation
 502                    string strarg = PythonOps.ToString(argsEnumerator.Current);
 503                    if (space) {
 504                        sb.Append(' ');
 505                    }
 506                    if (strarg.IndexOf(' ') != -1) {
 507                        sb.Append('"');
 508                        sb.Append(strarg);
 509                        sb.Append('"');
 510                    } else {
 511                        sb.Append(strarg);
 512                    }
 513                    space = true;
 514                }
 515            } finally {
 516                IDisposable disposable = argsEnumerator as IDisposable;
 517                if (disposable != null) disposable.Dispose();
 518            }
 519
 520            if (sb == null) return "";
 521            return sb.ToString();
 522        }
 523
 524#if !SILVERLIGHT
 525        public static void startfile(string filename, [DefaultParameterValue("open")]string operation) {
 526            System.Diagnostics.Process process = new System.Diagnostics.Process();
 527            process.StartInfo.FileName = filename;
 528            process.StartInfo.UseShellExecute = true;
 529            process.StartInfo.Verb = operation;
 530            try {
 531
 532                process.Start();
 533            } catch (Exception e) {
 534                throw ToPythonException(e);
 535            }
 536        }
 537#endif
 538
 539        [PythonType]
 540        public class stat_result : ISequence {
 541            private readonly object _mode, _size, _atime, _mtime, _ctime, _st_atime, _st_mtime, _st_ctime, _ino, _dev, _nlink, _uid, _gid;
 542
 543            public const int n_fields = 13;
 544            public const int n_sequence_fields = 10;
 545            public const int n_unnamed_fields = 3;
 546
 547            internal stat_result(int mode) : this(mode, BigInteger.Zero, BigInteger.Zero, BigInteger.Zero, BigInteger.Zero) {
 548                _mode = mode;
 549            }
 550
 551            internal stat_result(int mode, BigInteger size, BigInteger st_atime, BigInteger st_mtime, BigInteger st_ctime) {
 552                _mode = mode;
 553                _size = size;
 554                _st_atime = _atime = TryShrinkToInt(st_atime);
 555                _st_mtime = _mtime = TryShrinkToInt(st_mtime);
 556                _st_ctime = _ctime = TryShrinkToInt(st_ctime);
 557
 558                _ino = _dev = _nlink = _uid = _gid = RuntimeHelpers.Int32ToObject(0);                
 559            }
 560
 561            public stat_result(CodeContext/*!*/ context, ISequence statResult, [DefaultParameterValue(null)]PythonDictionary dict) {
 562                // dict is allowed by CPython's stat_result, but doesn't seem to do anything, so we ignore it here.
 563
 564                if (statResult.__len__() < 10) {
 565                    throw PythonOps.TypeError("stat_result() takes an at least 10-sequence ({0}-sequence given)", statResult.__len__());
 566                }
 567
 568                _mode = statResult[0];
 569                _ino = statResult[1];
 570                _dev = statResult[2];
 571                _nlink = statResult[3];
 572                _uid = statResult[4];
 573                _gid = statResult[5];
 574                _size = statResult[6];
 575                _atime = statResult[7];
 576                _mtime = statResult[8];
 577                _ctime = statResult[9];
 578
 579                object dictTime;
 580                if (statResult.__len__() >= 11) {
 581                    _st_atime = TryShrinkToInt(statResult[10]);
 582                } else if (TryGetDictValue(dict, "st_atime", out dictTime)) {
 583                    _st_atime = dictTime;
 584                } else {
 585                    _st_atime = TryShrinkToInt(_atime);
 586                }
 587
 588                if (statResult.__len__() >= 12) {
 589                    _st_mtime = TryShrinkToInt(statResult[11]);
 590                } else if (TryGetDictValue(dict, "st_mtime", out dictTime)) {
 591                    _st_mtime = dictTime;
 592                } else { 
 593                    _st_mtime = TryShrinkToInt(_mtime);
 594                }
 595
 596                if (statResult.__len__() >= 13) {
 597                    _st_ctime = TryShrinkToInt(statResult[12]);
 598                } else if (TryGetDictValue(dict, "st_ctime", out dictTime)) {
 599                    _st_ctime = dictTime;
 600                } else { 
 601                    _st_ctime = TryShrinkToInt(_ctime);
 602                }
 603            }
 604
 605            private static bool TryGetDictValue(PythonDictionary dict, string name, out object dictTime) {
 606                if (dict != null && dict.TryGetValue(name, out dictTime)) {
 607                    dictTime = TryShrinkToInt(dictTime);
 608                    return true;
 609                }
 610
 611                dictTime = null;
 612                return false;
 613            }
 614
 615            private static object TryShrinkToInt(object value) {
 616                BigInteger bi = value as BigInteger;
 617
 618                if (Object.ReferenceEquals(bi, null)) {
 619                    return value;
 620                }
 621                
 622                return BigIntegerOps.__int__(bi);
 623            }
 624
 625            public object st_atime {
 626                get {
 627                    return _st_atime;
 628                }
 629            }
 630
 631            public object st_ctime {
 632                get {
 633                    return _st_ctime;
 634                }
 635            }
 636
 637
 638            public object st_mtime {
 639                get {
 640                    return _st_mtime;
 641                }
 642            }
 643
 644            public object st_dev {
 645                get {
 646                    return TryShrinkToInt(_dev);
 647                }
 648            }
 649
 650            public object st_gid {
 651                get {
 652                    return _gid;
 653                }
 654            }
 655
 656            public object st_ino {
 657                get {
 658                    return _ino;
 659                }
 660            }
 661
 662            public object st_mode {
 663                get {
 664                    return TryShrinkToInt(_mode);
 665                }
 666            }
 667
 668            public object st_nlink {
 669                get {
 670                    return TryShrinkToInt(_nlink);
 671                }
 672            }
 673
 674            public object st_size {
 675                get {
 676                    return _size;
 677                }
 678            }
 679
 680            public object st_uid {
 681                get {
 682                    return _uid;
 683                }
 684            }
 685
 686            public override string ToString() {
 687                return MakeTuple().ToString();
 688            }
 689
 690            public string/*!*/ __repr__() {
 691                return ToString();
 692            }
 693
 694            public PythonTuple __reduce__() {
 695                PythonDictionary timeDict = new PythonDictionary(3);
 696                timeDict["st_atime"] = st_atime;
 697                timeDict["st_ctime"] = st_ctime;
 698                timeDict["st_mtime"] = st_mtime;
 699
 700                return PythonTuple.MakeTuple(
 701                    DynamicHelpers.GetPythonTypeFromType(typeof(stat_result)),
 702                    PythonTuple.MakeTuple(MakeTuple(), timeDict)
 703                );
 704            }
 705
 706            #region ISequence Members
 707
 708            //public object AddSequence(object other) {
 709            //    return MakeTuple().AddSequence(other);
 710            //}
 711
 712            //public object MultiplySequence(object count) {
 713            //    return MakeTuple().MultiplySequence(count);
 714            //}
 715
 716            public object this[int index] {
 717                get {
 718                    return MakeTuple()[index];
 719                }
 720            }
 721
 722            public object this[Slice slice] {
 723                get {
 724                    return MakeTuple()[slice];
 725                }
 726            }
 727
 728            public object __getslice__(int start, int stop) {
 729                return MakeTuple().__getslice__(start, stop);
 730            }
 731
 732            public int __len__() {
 733                return MakeTuple().__len__();
 734            }
 735
 736            public bool __contains__(object item) {
 737                return ((ICollection<object>)MakeTuple()).Contains(item);
 738            }
 739
 740            #endregion
 741
 742            private PythonTuple MakeTuple() {
 743                return PythonTuple.MakeTuple(
 744                    st_mode,
 745                    st_ino,
 746                    st_dev,
 747                    st_nlink,
 748                    st_uid,
 749                    st_gid,
 750                    st_size,
 751                    _atime,
 752                    _mtime,
 753                    _ctime
 754                );
 755            }
 756
 757            #region Object overrides
 758
 759            public override bool Equals(object obj) {
 760                if (obj is stat_result) {
 761                    return MakeTuple().Equals(((stat_result)obj).MakeTuple());
 762                } else {
 763                    return MakeTuple().Equals(obj);
 764                }
 765
 766            }
 767
 768            public override int GetHashCode() {
 769                return MakeTuple().GetHashCode();
 770            }
 771
 772            #endregion
 773        }
 774
 775        private static bool HasExecutableExtension(string path) {
 776            string extension = Path.GetExtension(path).ToLower(CultureInfo.InvariantCulture);
 777            return (extension == ".exe" || extension == ".dll" || extension == ".com" || extension == ".bat");
 778        }
 779
 780        [Documentation("stat(path) -> stat result\nGathers statistics about the specified file or directory")]
 781        public static object stat(string path) {
 782            if (path == null) throw PythonOps.TypeError("expected string, got NoneType");
 783
 784            stat_result sr;
 785
 786            try {
 787                FileInfo fi = new FileInfo(path);
 788                int mode = 0;
 789                long size;
 790
 791                if (Directory.Exists(path)) {
 792                    size = 0;
 793                    mode = 0x4000 | S_IEXEC;
 794                } else if (File.Exists(path)) {
 795                    size = fi.Length;
 796                    mode = 0x8000;
 797                    if (HasExecutableExtension(path)) {
 798                        mode |= S_IEXEC;
 799                    }
 800                } else {
 801                    throw PythonExceptions.CreateThrowable(WindowsError, PythonErrorNumber.ENOENT, "file does not exist: " + path);
 802                }
 803
 804                long st_atime = (long)PythonTime.TicksToTimestamp(fi.LastAccessTime.ToUniversalTime().Ticks);
 805                long st_ctime = (long)PythonTime.TicksToTimestamp(fi.CreationTime.ToUniversalTime().Ticks);
 806                long st_mtime = (long)PythonTime.TicksToTimestamp(fi.LastWriteTime.ToUniversalTime().Ticks);
 807                mode |= S_IREAD;
 808                if ((fi.Attributes & FileAttributes.ReadOnly) == 0) {
 809                    mode |= S_IWRITE;
 810                }
 811
 812                sr = new stat_result(mode, size, st_atime, st_mtime, st_ctime);
 813            } catch (ArgumentException) {
 814                throw PythonExceptions.CreateThrowable(WindowsError, PythonErrorNumber.EINVAL, "The path is invalid: " + path);
 815            } catch (Exception e) {
 816                throw ToPythonException(e);
 817            }
 818
 819            return sr;
 820        }
 821
 822        private static PythonType WindowsError {
 823            get {
 824#if !SILVERLIGHT
 825                return PythonExceptions.WindowsError;
 826#else
 827                return PythonExceptions.OSError;
 828#endif
 829            }
 830        }
 831
 832#if !SILVERLIGHT
 833        [Documentation("system(command) -> int\nExecute the command (a string) in a subshell.")]
 834        public static int system(string command) {
 835            ProcessStartInfo psi = GetProcessInfo(command);
 836            psi.CreateNoWindow = false;
 837
 838            Process process = Process.Start(psi);
 839            if (process == null) {
 840                return -1;
 841            }
 842            process.WaitForExit();
 843            return process.ExitCode;
 844        }
 845
 846        public static string tempnam(CodeContext/*!*/ context) {
 847            return tempnam(context, null);
 848        }
 849
 850        public static string tempnam(CodeContext/*!*/ context, string dir) {
 851            return tempnam(context, null, null);
 852        }
 853
 854        public static string tempnam(CodeContext/*!*/ context, string dir, string prefix) {
 855            PythonOps.Warn(context, PythonExceptions.RuntimeWarning, "tempnam is a potential security risk to your program");
 856
 857            try {
 858                dir = Path.GetTempPath(); // Reasonably consistent with CPython behavior under Windows
 859
 860                return Path.GetFullPath(Path.Combine(dir, prefix ?? String.Empty) + Path.GetRandomFileName());
 861            } catch (Exception e) {
 862                throw ToPythonException(e);
 863            }
 864        }
 865
 866        public static object times() {
 867            System.Diagnostics.Process p = System.Diagnostics.Process.GetCurrentProcess();
 868
 869            return PythonTuple.MakeTuple(p.UserProcessorTime.TotalSeconds,
 870                p.PrivilegedProcessorTime.TotalSeconds,
 871                0,  // child process system time
 872                0,  // child process os time
 873                DateTime.Now.Subtract(p.StartTime).TotalSeconds);
 874        }
 875#endif
 876
 877#if !SILVERLIGHT
 878        public static PythonFile/*!*/ tmpfile(CodeContext/*!*/ context) {
 879            try {
 880                FileStream sw = new FileStream(Path.GetTempFileName(), FileMode.Open, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose);
 881
 882                PythonFile res = PythonFile.Create(context, sw, sw.Name, "w+b");
 883                return res;
 884            } catch (Exception e) {
 885                throw ToPythonException(e);
 886            }
 887        }
 888
 889        public static string/*!*/ tmpnam(CodeContext/*!*/ context) {
 890            PythonOps.Warn(context, PythonExceptions.RuntimeWarning, "tmpnam is a potential security risk to your program");
 891            return Path.GetFullPath(Path.GetTempPath() + Path.GetRandomFileName());
 892        }
 893#endif
 894
 895        public static void unlink(string path) {
 896            UnlinkWorker(path);
 897        }
 898
 899        private static void UnlinkWorker(string path) {
 900            if (path == null) throw new ArgumentNullException("path");
 901
 902            if (!File.Exists(path)) {
 903                throw PythonExceptions.CreateThrowable(WindowsError, PythonErrorNumber.ENOENT, "The file could not be found for deletion: " + path);
 904            }
 905
 906            try {
 907                File.Delete(path);
 908            } catch (Exception e) {
 909                throw ToPythonException(e);
 910            }
 911        }
 912
 913#if !SILVERLIGHT
 914        public static void unsetenv(string varname) {
 915            System.Environment.SetEnvironmentVariable(varname, null);
 916        }
 917#endif
 918
 919        public static object urandom(int n) {
 920            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
 921            byte[] data = new byte[n];
 922            rng.GetBytes(data);
 923
 924            return PythonBinaryReader.PackDataIntoString(data, n);
 925        }
 926
 927        private static readonly object _umaskKey = new object();
 928
 929        public static int umask(CodeContext/*!*/ context, int mask) {
 930            mask &= 0x180;
 931            object oldMask = PythonContext.GetContext(context).GetSetModuleState(_umaskKey, mask);
 932            if (oldMask == null) {
 933                return 0;
 934            } else {
 935                return (int)oldMask;
 936            }
 937        }
 938
 939#if !SILVERLIGHT
 940        public static void utime(string path, PythonTuple times) {
 941            try {
 942                FileInfo fi = new FileInfo(path);
 943                if (times == null) {
 944                    fi.LastAccessTime = DateTime.Now;
 945                    fi.LastWriteTime = DateTime.Now;
 946                } else if (times.__len__() == 2) {
 947                    DateTime atime = new DateTime(PythonTime.TimestampToTicks(Converter.ConvertToDouble(times[0])), DateTimeKind.Utc);
 948                    DateTime mtime = new DateTime(PythonTime.TimestampToTicks(Converter.ConvertToDouble(times[1])), DateTimeKind.Utc);
 949
 950                    fi.LastAccessTime = atime;
 951                    fi.LastWriteTime = mtime;
 952                } else {
 953                    throw PythonOps.TypeError("times value must be a 2-value tuple (atime, mtime)");
 954                }
 955            } catch (Exception e) {
 956                throw ToPythonException(e);
 957            }
 958        }
 959
 960        public static PythonTuple waitpid(int pid, object options) {
 961            System.Diagnostics.Process process = System.Diagnostics.Process.GetProcessById(pid);
 962            if (process == null) {
 963                throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, PythonErrorNumber.ECHILD, "Cannot find process " + pid);
 964            }
 965            process.WaitForExit();
 966            return PythonTuple.MakeTuple(pid, process.ExitCode);
 967        }
 968#endif
 969
 970        public static void write(CodeContext/*!*/ context, int fd, string text) {
 971            try {
 972                PythonContext pythonContext = PythonContext.GetContext(context);
 973                PythonFile pf = pythonContext.FileManager.GetFileFromId(pythonContext, fd);
 974                pf.write(text);
 975            } catch (Exception e) {
 976                throw ToPythonException(e);
 977            }
 978        }
 979
 980        public const int O_APPEND = 0x8;
 981        public const int O_CREAT = 0x100;
 982        public const int O_TRUNC = 0x200;
 983
 984        public const int O_EXCL = 0x400;
 985        public const int O_NOINHERIT = 0x80;
 986
 987        public const int O_RANDOM = 0x10;
 988        public const int O_SEQUENTIAL = 0x20;
 989
 990        public const int O_SHORT_LIVED = 0x1000;
 991        public const int O_TEMPORARY = 0x40;
 992
 993        public const int O_WRONLY = 0x1;
 994        public const int O_RDONLY = 0x0;
 995        public const int O_RDWR = 0x2;
 996
 997        public const int O_BINARY = 0x8000;
 998        public const int O_TEXT = 0x4000;
 999
1000        public const int P_WAIT = 0;
1001        public const int P_NOWAIT = 1;
1002        public const int P_NOWAITO = 3;
1003
1004        // Not implemented:
1005        // public static object P_OVERLAY = 2;
1006        // public static object P_DETACH = 4;
1007
1008        #endregion
1009
1010        #region Private implementation details
1011
1012        private static Exception ToPythonException(Exception e) {
1013            if (e is ArgumentException || e is ArgumentNullException || e is ArgumentTypeException) {
1014                // rethrow reasonable exceptions
1015                return ExceptionHelpers.UpdateForRethrow(e);
1016            }
1017
1018            string message = e.Message;
1019            int errorCode;
1020
1021#if !SILVERLIGHT
1022            bool isWindowsError = false;
1023            Win32Exception winExcep = e as Win32Exception;
1024            if (winExcep != null) {
1025                errorCode = ToPythonErrorCode(winExcep.NativeErrorCode);
1026                message = GetFormattedException(e, errorCode);
1027                isWindowsError = true;
1028            } else {
1029#endif
1030                errorCode = System.Runtime.InteropServices.Marshal.GetHRForException(e);
1031                if ((errorCode & ~0xfff) == (unchecked((int)0x80070000))) {
1032                    // Win32 HR, translate HR to Python error code if possible, otherwise
1033                    // report the HR.
1034                    errorCode = ToPythonErrorCode(errorCode & 0xfff);
1035                    message = GetFormattedException(e, errorCode);
1036#if !SILVERLIGHT
1037                    isWindowsError = true;
1038#endif
1039                }
1040#if !SILVERLIGHT
1041            }
1042
1043            if (isWindowsError) {
1044                return PythonExceptions.CreateThrowable(WindowsError, errorCode, message);
1045            }
1046#endif
1047
1048            return PythonExceptions.CreateThrowable(PythonExceptions.OSError, errorCode, message);
1049        }
1050
1051        private static string GetFormattedException(Exception e, int hr) {
1052            return "[Errno " + hr.ToString() + "] " + e.Message;
1053        }
1054
1055        private static int ToPythonErrorCode(int win32ErrorCode) {
1056            switch (win32ErrorCode) {
1057                case ERROR_FILE_EXISTS:       return PythonErrorNumber.EEXIST; 
1058                case ERROR_ACCESS_DENIED:     return PythonErrorNumber.EACCES; 
1059                case ERROR_DLL_NOT_FOUND:
1060                case ERROR_FILE_NOT_FOUND:
1061                case ERROR_PATH_NOT_FOUND:    return PythonErrorNumber.ENOENT; 
1062                case ERROR_CANCELLED:         return PythonErrorNumber.EINTR; 
1063                case ERROR_NOT_ENOUGH_MEMORY: return PythonErrorNumber.ENOMEM; 
1064                case ERROR_SHARING_VIOLATION: return PythonErrorNumber.EBUSY;  
1065                case ERROR_NO_ASSOCIATION:    return PythonErrorNumber.EINVAL; 
1066            }
1067            return win32ErrorCode;
1068        }
1069
1070        // Win32 error codes
1071        private const int ERROR_FILE_EXISTS = 80;
1072        private const int ERROR_ACCESS_DENIED = 5; // Access to the specified file is denied. 
1073        private const int ERROR_FILE_NOT_FOUND = 2; //The specified file was not found. 
1074        private const int ERROR_PATH_NOT_FOUND = 3; // The specified path was not found. 
1075        private const int ERROR_NO_ASSOCIATION = 1155; //There is no application associated with the given file name extension. 
1076        private const int ERROR_DLL_NOT_FOUND = 1157; // One of the library files necessary to run the application can't be found. 
1077        private const int ERROR_CANCELLED = 1223; // The function prompted the user for additional information, but the user canceled the request. 
1078        private const int ERROR_NOT_ENOUGH_MEMORY = 8; // There is not enough memory to perform the specified action. 
1079        private const int ERROR_SHARING_VIOLATION = 32; //A sharing violation occurred. 
1080        private const int ERROR_ALREADY_EXISTS = 183;
1081
1082        private const int S_IWRITE = 0x80 + 0x10 + 0x02; // owner / group / world
1083        private const int S_IREAD = 0x100 + 0x20 + 0x04; // owner / group / world
1084        private const int S_IEXEC = 0x40 + 0x08 + 0x01; // owner / group / world
1085
1086        public const int F_OK = 0;
1087        public const int X_OK = 1;
1088        public const int W_OK = 2;
1089        public const int R_OK = 4;
1090
1091        private static void addBase(string[] files, List ret) {
1092            foreach (string file in files) {
1093                ret.AddNoLock(Path.GetFileName(file));
1094            }
1095        }
1096
1097        private static FileMode FileModeFromFlags(int flags) {
1098            if ((flags & O_APPEND) != 0) return FileMode.Append;
1099            if ((flags & O_CREAT) != 0) return FileMode.CreateNew;
1100            if ((flags & O_TRUNC) != 0) return FileMode.Truncate;
1101            return FileMode.Open;
1102        }
1103
1104        private static FileAccess FileAccessFromFlags(int flags) {
1105            if ((flags & O_RDWR) != 0) return FileAccess.ReadWrite;
1106            if ((flags & O_WRONLY) != 0) return FileAccess.Write;
1107
1108            return FileAccess.Read;
1109        }
1110#if !SILVERLIGHT // Processes
1111        [PythonType]
1112        private class POpenFile : PythonFile {
1113            private Process _process;
1114
1115            public static object __new__(CodeContext/*!*/ context, string command, Process process, Stream stream, string mode) {
1116                return new POpenFile(context, command, process, stream, mode);
1117            }
1118
1119            internal POpenFile(CodeContext/*!*/ context, string command, Process process, Stream stream, string mode) 
1120                : base(PythonContext.GetContext(context)) {
1121                __init__(stream, PythonContext.GetContext(context).DefaultEncoding, command, mode);
1122                this._process = process;
1123            }
1124
1125            public override object close() {
1126                base.close();
1127
1128                if (_process.HasExited && _process.ExitCode != 0) {
1129                    return _process.ExitCode;
1130                }
1131
1132                return null;
1133            }
1134        }
1135
1136        private static ProcessStartInfo GetProcessInfo(string command) {
1137            // TODO: always run through cmd.exe ?
1138            command = command.Trim();
1139            string baseCommand, args;
1140            if (!TryGetExecutableCommand(command, out baseCommand, out args)) {
1141                if (!TryGetShellCommand(command, out baseCommand, out args)) {
1142                    throw PythonOps.WindowsError("The system can not find command '{0}'", command);
1143                }
1144            }
1145
1146            ProcessStartInfo psi = new ProcessStartInfo(baseCommand, args);
1147            psi.UseShellExecute = false;
1148
1149            return psi;
1150        }
1151
1152        private static bool TryGetExecutableCommand(string command, out string baseCommand, out string args) {
1153            baseCommand = command;
1154            args = String.Empty;
1155            int pos;
1156
1157            if (command[0] == '\"') {
1158                for (pos = 1; pos < command.Length; pos++) {
1159                    if (command[pos] == '\"') {
1160                        baseCommand = command.Substring(1, pos - 1).Trim();
1161                        if (pos + 1 < command.Length) {
1162                            args = command.Substring(pos + 1);
1163                        }
1164                        break;
1165                    }
1166                }
1167                if (pos == command.Length)
1168                    throw PythonOps.ValueError("mismatch quote in command");
1169            } else {
1170                pos = command.IndexOf(' ');
1171                if (pos != -1) {
1172                    baseCommand = command.Substring(0, pos);
1173                    // pos won't be the last one
1174                    args = command.Substring(pos + 1);
1175                }
1176            }
1177            string fullpath = Path.GetFullPath(baseCommand);
1178            if (File.Exists(fullpath)) {
1179                baseCommand = fullpath;
1180                return true;
1181            }
1182
1183            // TODO: need revisit
1184            string sysdir = System.Environment.GetFolderPath(System.Environment.SpecialFolder.System);
1185            foreach (string suffix in new string[] { string.Empty, ".com", ".exe", "cmd", ".bat" }) {
1186                fullpath = Path.Combine(sysdir, baseCommand + suffix);
1187                if (File.Exists(fullpath)) {
1188                    baseCommand = fullpath;
1189                    return true;
1190                }
1191            }
1192
1193            return false;
1194        }
1195
1196        private static bool TryGetShellCommand(string command, out string baseCommand, out string args) {
1197            baseCommand = Environment.GetEnvironmentVariable("COMSPEC");
1198            args = String.Empty;
1199            if (baseCommand == null) {
1200                baseCommand = Environment.GetEnvironmentVariable("SHELL");
1201                if (baseCommand == null) {
1202                    return false;
1203                }
1204                args = String.Format("-c \"{0}\"", command);
1205            } else {
1206                args = String.Format("/c {0}", command);
1207            }
1208            return true;
1209        }
1210
1211        private static Exception DirectoryExists() {
1212            PythonExceptions._WindowsError err = new PythonExceptions._WindowsError();
1213            err.__init__(ERROR_ALREADY_EXISTS, "directory already exists");
1214            err.errno = PythonErrorNumber.EEXIST;
1215
1216            return PythonExceptions.ToClr(err);
1217        }
1218#endif
1219
1220        #endregion
1221    }
1222}