github.com/remobjects/goldbaselibrary@v0.0.0-20230924164425-d458680a936b/Source/Gold/os.pas (about)

     1  namespace go.os;
     2  
     3  {$IF ISLAND}
     4  uses
     5    RemObjects.Elements.System
     6  {$IF DARWIN}
     7    , CoreFoundation, Security,
     8  {$ENDIF}
     9    ;
    10  {$ELSEIF ECHOES}
    11  uses
    12    System.IO, System.Diagnostics, System.Collections.Generic, System.Linq;
    13  {$ENDIF}
    14  
    15  
    16  
    17  // Flags to OpenFile wrapping those of the underlying system. Not all
    18  // flags may be implemented on a given system.
    19  const
    20      // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
    21  O_RDONLY : Integer = go.syscall.O_RDONLY; // open the file read-only.
    22  O_WRONLY : Integer = go.syscall.O_WRONLY; // open the file write-only.
    23  O_RDWR   : Integer = go.syscall.O_RDWR  ; // open the file read-write.
    24      // The remaining values may be or'ed in to control behavior.
    25  O_APPEND : Integer = go.syscall.O_APPEND; // append data to the file when writing.
    26  O_CREATE : Integer = go.syscall.O_CREAT ; // create a new file if none exists.
    27  O_EXCL   : Integer = go.syscall.O_EXCL  ; // used with O_CREATE, file must not exist.
    28  O_SYNC   : Integer = go.syscall.O_SYNC  ; // open for synchronous I/O.
    29  O_TRUNC  : Integer = go.syscall.O_TRUNC ; // if possible, truncate file when opened.
    30  
    31  
    32  // Seek whence values.
    33  //
    34  // Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.
    35  
    36  SEEK_SET : Integer = 0; // seek relative to the origin of the file
    37  SEEK_CUR : Integer = 1; // seek relative to the current offset
    38  SEEK_END : Integer = 2; // seek relative to the end
    39  
    40  
    41  
    42  // A FileMode represents a file's mode and permission bits.
    43  // The bits have the same definition on all systems, so that
    44  // information about files can be moved from one system
    45  // to another portably. Not all bits apply to all systems.
    46  // The only required bit is ModeDir for directories.
    47  type
    48    ProcessType = public {$IFDEF ECHOES}System.Diagnostics.Process{$ELSE}RemObjects.Elements.System.Process{$ENDIF};
    49  
    50    [AliasSemantics]
    51    FileMode = public record
    52    public
    53      Value: UInt32;
    54      constructor; empty;
    55      constructor(aValue: UInt32); begin Value := aValue;end;
    56      method IsDir: Boolean;
    57      begin
    58        exit 0 <> (Value and ModeDir);
    59      end;
    60  
    61      method IsRegular: Boolean;
    62      begin
    63        exit 0 =  (Value and ModeType);
    64      end;
    65  
    66      method Perm: FileMode;
    67      begin
    68        exit Value and ModePerm;
    69      end;
    70    end;
    71  
    72  
    73  
    74  // The defined file mode bits are the most significant bits of the FileMode.
    75  // The nine least-significant bits are the standard Unix rwxrwxrwx permissions.
    76  // The values of these bits should be considered part of the public API and
    77  // may be used in wire protocols or disk representations: they must not be
    78  // changed, although new bits might be added.
    79  const
    80      // The single letters are the abbreviations
    81      // used by the String method's formatting.
    82  ModeDir    :FileMode  = 1 shl (32 - 1 - 0); // d: is a directory
    83  ModeAppend :FileMode= 1 shl (32 - 1 - 1); //a: append-only
    84  ModeExclusive :FileMode = 1 shl (32 - 1 - 2); //l: exclusive use
    85  ModeTemporary :FileMode = 1 shl (32 - 1 - 3); //T: temporary file; Plan 9 only
    86  ModeSymlink :FileMode = 1 shl (32 - 1 - 4); //L: symbolic link
    87  ModeDevice :FileMode = 1 shl (32 - 1 - 5); //D: device file
    88  ModeNamedPipe :FileMode = 1 shl (32 - 1 - 6); //p: named pipe (FIFO)
    89  ModeSocket  :FileMode= 1 shl (32 - 1 - 7); //S: Unix domain socket
    90  ModeSetuid :FileMode = 1 shl (32 - 1 - 8); //u: setuid
    91  ModeSetgid  :FileMode= 1 shl (32 - 1 - 9); //g: setgid
    92  ModeCharDevice :FileMode = 1 shl (32 - 1 - 10); //c: Unix character device, when ModeDevice is set
    93  ModeSticky :FileMode = 1 shl (32 - 1 - 11); //t: sticky
    94  ModeIrregular  :FileMode= 1 shl(32 - 1 - 12); //non-regular file; nothing else is known about this file
    95  
    96      // Mask for the type bits. For regular files, none will be set.
    97  ModeType :FileMode = ModeDir or ModeSymlink or ModeNamedPipe or ModeSocket or ModeDevice or ModeCharDevice or ModeIrregular;
    98  
    99  ModePerm :FileMode= $1FF;// 0777calc // Unix permission bits
   100  
   101  
   102  
   103  type
   104    go.internal.cpu.__Global = public partial class
   105    public
   106      const CacheLinePadSize = 32;
   107  
   108      class method doinit(); assembly;
   109      begin
   110  
   111      end;
   112    end;
   113    go.internal.poll.__Global = public partial class
   114    public
   115      class var ErrNoDeadline := go.errors.New("file type does not support deadline");
   116    end;
   117  
   118    __Global = public partial class
   119    public
   120      {$IF ISLAND AND WINDOWS}
   121      class property Stdin: File := new File(fs := new FileStream(rtl.GetStdHandle(rtl.STD_INPUT_HANDLE), FileAccess.Read)); lazy; readonly;
   122      class property Stderr: File := new File(fs := new FileStream(rtl.GetStdHandle(rtl.STD_ERROR_HANDLE), FileAccess.Write)); lazy; readonly;
   123      class property Stdout: File := new File(fs := new FileStream(rtl.GetStdHandle(rtl.STD_OUTPUT_HANDLE), FileAccess.Write)); lazy; readonly;
   124      {$ELSEIF ISLAND AND ANDROID}
   125      class property Stdin: File := new File(fs := new NullStream()); lazy; readonly;
   126      class property Stderr: File := new File(fs := new NullStream()); lazy; readonly;
   127      class property Stdout: File := new File(fs := new NullStream()); lazy; readonly;
   128      {$ELSEIF ISLAND AND POSIX}
   129      class property Stdin: File := new File(fs := new FileStream(rtl.stdin, FileAccess.Read)); lazy; readonly;
   130      class property Stderr: File := new File(fs := new FileStream(rtl.stderr, FileAccess.Write)); lazy; readonly;
   131      class property Stdout: File := new File(fs := new FileStream(rtl.stdout, FileAccess.Write)); lazy; readonly;
   132      {$ELSEIF ECHOES}
   133      class property Stdin: File := new File(fs := Console.OpenStandardInput); lazy; readonly;
   134      class property Stderr: File := new File(fs := Console.OpenStandardError); lazy; readonly;
   135      class property Stdout: File := new File(fs := Console.OpenStandardOutput); lazy; readonly;
   136      {$ENDIF}
   137  
   138      {$IF (NOT ECHOES) AND NOT (ISLAND AND WINDOWS)}
   139      class method removeAll(path: String): go.builtin.error;
   140      begin
   141        // TODO
   142      end;
   143      {$ENDIF}
   144    end;
   145  
   146    File = public partial class(go.io.ReaderAt, go.io.Reader, go.io.Writer)
   147    unit
   148      {$IF ECHOES}
   149      fs: Stream;
   150      {$ELSE}
   151      fs: RemObjects.Elements.System.Stream;
   152      {$ENDIF}
   153      path: String;
   154  
   155    public
   156      method Name: String;
   157      begin
   158        exit path;
   159      end;
   160  
   161      method ReadAt(p: go.builtin.Slice<go.builtin.byte>; off: go.builtin.int64): tuple of (go.builtin.int, go.builtin.error);
   162      begin
   163        var pp := fs.Position;
   164        fs.Position := off;
   165        try
   166          var lStart := p.fStart;
   167          var lCount := p.fCount;
   168          while lCount > 0 do begin
   169            var c := fs.Read(p.fArray, lStart, lCount);
   170            if c = 0  then
   171              exit (p.fCount - lCount, go.errors.New('EOF'));
   172            lStart := lStart + c;
   173            lCount := lCount - c;
   174          end;
   175        except
   176          on e: Exception do
   177           exit (-1, go.errors.New(e.Message));
   178        end;
   179        fs.Position := pp;
   180        exit (p.fCount, nil);
   181      end;
   182  
   183      method Readdirnames(n: Integer): tuple of (go.builtin.Slice<go.builtin.string>, go.builtin.error);
   184      begin
   185        if n < 0 then n := Int32.MaxValue;
   186        try
   187          {$IF ISLAND}
   188          var lFolder := new Folder(path);
   189          var lFiles := lFolder.GetFiles;
   190          var lSubDirs := lFolder.GetSubfolders;
   191          var res := new String[lFiles.Count + lSubDirs.Count];
   192          for i: Integer := 0 to lFiles.Count - 1 do
   193            res[i] := lFiles[i].ToString;
   194          for i: Integer := 0 to lSubDirs.Count - 1 do
   195            res[i + lFiles.Count] := lSubDirs[i].ToString;
   196          {$ELSEIF ECHOES}
   197          var res := Directory.GetFileSystemEntries(path);
   198          {$ENDIF}
   199  
   200          if res.Length > n then begin
   201            var r := new String[n];
   202            Array.Copy(res, r, n);
   203            res := r;
   204          end;
   205          exit (go.builtin.string.PlatformStringArrayToGoSlice(res), nil);
   206        except
   207          on e: Exception do
   208            exit (nil, go.Errors.New(e.Message));
   209        end;
   210      end;
   211  
   212  
   213      method Readdir(n: go.builtin.int): tuple of (go.builtin.Slice<FileInfo>, go.builtin.error);
   214      begin
   215        if n < 0 then n := Int32.MaxValue;
   216        try
   217          {$IF ISLAND}
   218          var lFolder := new Folder(path);
   219          var lFiles := lFolder.GetFiles;
   220          var lSubDirs := lFolder.GetSubfolders;
   221          var res := new String[lFiles.Count + lSubDirs.Count];
   222          for i: Integer := 0 to lFiles.Count - 1 do
   223            res[i] := lFiles[i].ToString;
   224          for i: Integer := 0 to lSubDirs.Count - 1 do
   225            res[i + lFiles.Count] := lSubDirs[i].ToString;
   226          {$ELSEIF ECHOES}
   227          var res := Directory.GetFileSystemEntries(path);
   228          {$ENDIF}
   229  
   230          if n > res.Length then n := res.Length;
   231          var lRes := new FileInfo[n];
   232          for i: Integer := 0 to n -1 do
   233            lRes[i] := new MyFileInfo(res[i]);
   234          exit (new go.builtin.Slice<FileInfo>(lRes), nil);
   235        except
   236          on e: Exception do
   237            exit (nil, go.Errors.New(e.Message));
   238        end;
   239      end;
   240  
   241      method Stat: tuple of (FileInfo, go.builtin.error);
   242      begin
   243        exit (new MyFileInfo(path), nil);
   244      end;
   245  
   246      method Close: go.builtin.error;
   247      begin
   248        disposeAndNil(fs);
   249  
   250        exit nil;
   251      end;
   252  
   253      method Sync();
   254      begin
   255  
   256      end;
   257  
   258      method &Read(p: go.builtin.Slice<go.builtin.byte>): tuple of (go.builtin.int, go.builtin.error);
   259      begin
   260        try
   261          var b := fs.Read(p.fArray, p.fStart, p.fCount);
   262          if b = -1 then exit (0, go.Errors.New('EOF'));
   263          exit (b, nil);
   264  
   265        except
   266          on e: Exception do
   267            exit (0, go.Errors.New(e.Message));
   268        end;
   269      end;
   270  
   271      method &Write(p: go.builtin.Slice<go.builtin.byte>): tuple of (go.builtin.int, go.builtin.error);
   272      begin
   273        try
   274           fs.Write(p.fArray, p.fStart, p.fCount);
   275          exit (p.fCount, nil);
   276  
   277        except
   278          on e: Exception do
   279            exit (0, go.Errors.New(e.Message));
   280        end;
   281      end;
   282  
   283      method WriteString(p: go.builtin.string): tuple of (go.builtin.int, go.builtin.error);
   284      begin
   285        exit &Write(p.Value);
   286      end;
   287  
   288      method &Seek(offset: go.builtin.int64; whence: go.builtin.int): tuple of (go.builtin.int64, go.builtin.error);
   289      begin
   290        try
   291          exit (fs.Seek(offset, if whence = 0 then SeekOrigin.Begin else if whence = 2 then SeekOrigin.End else SeekOrigin.Current), nil);
   292       except
   293         on e: Exception do
   294           exit (0, go.Errors.New(e.Message));
   295       end;
   296      end;
   297    end;
   298    method Getwd: tuple of (go.builtin.string, go.builtin.error); public;
   299    begin
   300      exit (Environment.CurrentDirectory, nil);
   301    end;
   302  
   303    method Chdir(p: String): go.builtin.error;
   304    begin
   305      try
   306        {$IFDEF ECHOES}
   307        Environment.CurrentDirectory := p;
   308        {$ELSE}
   309        raise new NotImplementedException;
   310        {$ENDIF}
   311      except
   312        on e: Exception do
   313          exit go.Errors.New(e.Message);
   314      end;
   315    end;
   316  
   317    method Getgid: Int64; public; empty;
   318    method Getuid: Int64; public; empty;
   319    method Geteuid: Int64; public; empty;
   320    method Getpid: Int64; public; empty;
   321    method Getppid: Int64; public; empty;
   322  
   323    method executable(): tuple of (go.builtin.string, go.builtin.error); public;
   324    begin
   325      {$IF ISLAND AND WINDOWS}
   326      var lBuffer := new Char[rtl.MAX_PATH + 1];
   327      rtl.GetModuleFileName(nil, @lBuffer[0], length(lBuffer));
   328      result := (new go.builtin.string(lBuffer), nil);
   329      {$ELSEIF ECHOES}
   330      var asm :=  &System.Reflection.Assembly.GetEntryAssembly();
   331      if asm = nil then exit ('', go.Errors.New('Unknown entry assembly'));
   332      exit (asm.Location, nil);
   333      {$ENDIF}
   334    end;
   335  
   336    method Mkdir(name: go.builtin.string; perm :FileMode): go.builtin.error;
   337    begin
   338      try
   339        {$IF ISLAND}
   340        Folder.CreateFolder(name, false);
   341        {$ELSEIF ECHOES}
   342        System.IO.Directory.CreateDirectory(name);
   343        {$ENDIF}
   344        exit nil;
   345      except
   346        on e: Exception do
   347          exit go.errors.New(e.Message);
   348        end;
   349    end;
   350  
   351    method Remove(s: go.builtin.string): go.builtin.error;
   352    begin
   353      try
   354        {$IF ISLAND}
   355        var lFolder := new Folder(s);
   356        lFolder.Delete;
   357        {$ELSEIF ECHOES}
   358        if System.IO.File.Exists(s) then
   359          System.IO.File.Delete(s)
   360        else if System.IO.Directory.Exists(s) then
   361          System.IO.Directory.Delete(s)
   362        else
   363          exit go.errors.New('Not found');
   364        {$ENDIF}
   365        exit nil;
   366      except
   367        on e: Exception do
   368          exit go.errors.New(e.Message);
   369      end;
   370    end;
   371  
   372    method fixRootDirectory(s: go.builtin.string): go.builtin.string;
   373    begin
   374      if defined('WINDOWS') or (defined('ECHOES') and (Environment.OSVersion.Platform = PlatformID.Win32NT)) then begin
   375        if s.Length <3 then
   376          s := s +'\';
   377      end;
   378      exit s;
   379    end;
   380  
   381    method &Create(name: go.builtin.string): tuple of (File, go.builtin.error); public;
   382  begin
   383    try
   384    {$IF ISLAND}
   385    exit (new File(fs := new FileStream(name, RemObjects.Elements.System.FileMode.Create, FileAccess.ReadWrite), path := name), nil);
   386    {$ELSEIF ECHOES}
   387    exit (new File(fs := System.IO.File.Create(name), path := name), nil);
   388    {$ENDIF}
   389    except
   390      on e: Exception do
   391        exit (nil, go.errors.New(e.Message));
   392     end;
   393  end;
   394  
   395  
   396  method NewFile(fd: go.builtin.uint64; name: go.builtin.string): File;public;
   397  begin
   398    raise new NotSupportedException;
   399  end;
   400  
   401  
   402  method &Open(name: go.builtin.string): tuple of (File, go.builtin.error);public;
   403  begin
   404    try
   405      {$IF ISLAND}
   406      if RemObjects.Elements.System.File.Exists(name) then
   407        exit (new File(fs := new FileStream(name, RemObjects.Elements.System.FileMode.Open, FileAccess.Read), path := name), nil)
   408      else
   409        exit (nil, go.errors.New('File do not exists!'));
   410      {$ELSEIF ECHOES}
   411      exit (new File(fs := System.IO.File.OpenRead(name), path := name), nil);
   412      {$ENDIF}
   413    except
   414      on e: Exception do
   415        exit (nil, go.errors.New(e.Message));
   416     end;
   417  end;
   418  
   419  method &OpenFile(name: go.builtin.string; aFlags: Integer; perm: FileMode): tuple of (File, go.builtin.error);public;
   420  begin
   421    try
   422      {$IF ISLAND}
   423      var lCreate := if (O_CREATE and aFlags) <> 0 then (if (O_EXCL and aFlags) <> 0 then RemObjects.Elements.System.FileMode.CreateNew else RemObjects.Elements.System.FileMode.Create) else
   424        if( O_APPEND and aFlags) <> 0 then RemObjects.Elements.System.FileMode.Open else RemObjects.Elements.System.FileMode.Truncate;
   425      var lAccess :=
   426      if (O_RDONLY and aFlags) <> 0 then RemObjects.Elements.System.FileAccess.Read else
   427        if (O_WRONLY and aFlags) <> 0 then RemObjects.Elements.System.FileAccess.Write else RemObjects.Elements.System.FileAccess.ReadWrite;
   428      exit (new File(fs := new FileStream(name, lCreate, lAccess), path := name), nil);
   429      {$ELSEIF ECHOES}
   430      var lCreate := if (O_CREATE and aFlags) <> 0 then (if (O_EXCL and aFlags) <> 0 then System.IO.FileMode.CreateNew else System.IO.FileMode.Create) else
   431        if( O_APPEND and aFlags) <>0 then System.IO.FileMode.Append else System.IO.FileMode.Open;
   432      var lAccess :=
   433      if (O_RDONLY and aFlags) <> 0 then System.IO.FileAccess.Read else
   434        if (O_WRONLY and aFlags) <> 0 then System.IO.FileAccess.Write else System.IO.FileAccess.ReadWrite;
   435      exit (new File(fs := new System.IO.FileStream(name, lCreate, lAccess), path := name), nil);
   436      {$ENDIF}
   437    except
   438      on e: Exception do
   439        exit (nil, go.errors.New(e.Message));
   440    end;
   441  end;
   442  method hostname: tuple of (go.builtin.string, go.builtin.error);
   443  begin
   444    {$IF ISLAND}
   445    raise new NotImplementedException;
   446    {$ELSEIF ECHOES}
   447    exit (Environment.MachineName, nil);
   448    {$ENDIF}
   449  end;
   450  
   451  {$IF ISLAND}
   452  method GetArgs: array of String;
   453  begin
   454    {$IF WINDOWS}
   455    var lTotal: Int32;
   456    var lRawArgs := rtl.CommandLineToArgvW(rtl.GetCommandLineW(), @lTotal);
   457    var lArgs := new String[lTotal - 1];
   458    for i: Integer := 1 to lTotal - 1 do
   459      lArgs[i-1] := String.FromPChar(lRawArgs[i]);
   460    exit lArgs;
   461    {$ELSE}
   462    var lArgs := new String[ExternalCalls.nargs];
   463    for i: Integer := 0 to ExternalCalls.nargs - 1 do
   464      lArgs[i] := String.FromPAnsiChar(ExternalCalls.args[i]);
   465    exit lArgs;
   466    {$ENDIF}
   467  end;
   468  {$ENDIF}
   469  
   470  var Args: go.builtin.Slice<go.builtin.string> := go.builtin.string.PlatformStringArrayToGoSlice({$IF ISLAND}GetArgs{$ELSEIF ECHOES}Environment.GetCommandLineArgs{$ENDIF});
   471  
   472  method &Exit(i: Integer);
   473  begin
   474    {$IF ISLAND}
   475    raise new NotImplementedException;
   476    {$ELSEIF ECHOES}
   477    Environment.Exit(i);
   478    {$ENDIF}
   479  end;
   480  
   481  method lstatNolog(fn: go.builtin.string): tuple of(FileInfo, go.builtin.error);
   482  begin
   483    {$IF ISLAND}
   484    var lFile := new RemObjects.Elements.System.File(fn);
   485    var lFolder := new Folder(fn);
   486    if lFile.Exists or lFolder.Exists then
   487      exit (new MyFileInfo(fn), nil);
   488    {$ELSEIF ECHOES}
   489    if System.IO.File.Exists(fn) or System.IO.Directory.Exists(fn) then
   490      exit (new MyFileInfo(fn), nil);
   491    {$ENDIF}
   492    exit (nil, go.errors.New('Not found '+fn));
   493  end;
   494  
   495  method statNolog(fn: go.builtin.string): tuple of (FileInfo, go.builtin.error);
   496  begin
   497    {$IF ISLAND}
   498    var lFile := new RemObjects.Elements.System.File(fn);
   499    var lFolder := new Folder(fn);
   500    if lFile.Exists or lFolder.Exists then
   501      exit (new MyFileInfo(fn), nil);
   502    {$ELSEIF ECHOES}
   503    if System.IO.File.Exists(fn) or System.IO.Directory.Exists(fn) then
   504      exit (new MyFileInfo(fn), nil);
   505    {$ENDIF}
   506    exit (nil, go.errors.New('Not found '+fn));
   507  end;
   508  
   509  type
   510  
   511    FileInfo = public interface
   512      method Name: go.builtin.string;
   513      method Size: go.builtin.int64;
   514      method Mode: FileMode;
   515      method ModTime: go.time.Time;
   516      method IsDir: Boolean;
   517      method Sys: Object;
   518    end;
   519  
   520    MyFileInfo = class(FileInfo)
   521    private
   522      fFile: String;
   523    public
   524      constructor(aFile: String); begin fFile := aFile; end;
   525      method Name: go.builtin.string; begin exit Path.GetFileName(fFile); end;
   526      method Size: go.builtin.int64; begin {$IF ISLAND}exit new RemObjects.Elements.System.File(fFile).Length;{$ELSEIF ECHOES}exit new System.IO.FileInfo(fFile).Length;{$ENDIF} end;
   527      method Mode: FileMode;
   528      begin
   529        {$IF ISLAND}
   530        raise new NotImplementedException;
   531        {$ELSEIF ECHOES}
   532        var lAtt := System.IO.File.GetAttributes(fFile);
   533        result := new FileMode(Value := 0);
   534        if FileAttributes.Directory in lAtt then
   535          result.Value := result.Value or Integer(ModeDir);
   536        {$ENDIF}
   537      end;
   538      method ModTime: go.time.Time;
   539      begin
   540        {$IF ISLAND}
   541        raise new NotImplementedException;
   542        {$ELSEIF ECHOES}
   543        var lLast :=  System.IO.File.GetLastWriteTimeUtc(fFile);
   544        exit go.time.Date(lLast.Year, lLast.Month, lLast.Day, lLast.Hour, lLast.Minute, lLast.Second, lLast.Millisecond * 1000000, go.time.UTC);
   545        {$ENDIF}
   546      end;
   547      method IsDir: Boolean; begin {$IF ISLAND}exit new Folder(fFile).Exists;{$ELSEIF ECHOES}exit FileAttributes.Directory in System.IO.File.GetAttributes(fFile);{$ENDIF} end;
   548      method Sys: Object; begin exit fFile; end;
   549    end;
   550  
   551    [ValueTypeSemantics]
   552    fileStat = public class
   553    end;
   554    var PathSeparator: Char := {$IF ISLAND}Path.DirectorySeparatorChar{$ELSEIF ECHOES}System.IO.Path.DirectorySeparatorChar{$ENDIF}; readonly; public;
   555    var PathListSeparator:  Char := {$IF ISLAND}Path.ListSeparator{$ELSEIF ECHOES}System.IO.Path.PathSeparator{$ENDIF}; readonly;public;
   556  
   557    method IsPathSeparator(c: Char): Boolean; begin exit c = PathSeparator; end;
   558    method Readlink(name: go.builtin.string): tuple of (go.builtin.string, go.builtin.error);
   559    begin
   560      exit ('', go.Errors.New('Not supported'));
   561    end;
   562  type
   563    DiscardWriter = class(go.io.Writer)
   564    public
   565      method &Write(p: go.builtin.Slice<go.builtin.byte>): tuple of (go.builtin.int, go.builtin.error);
   566      begin
   567        exit (p.Length, nil);
   568      end;
   569    end;
   570    MyNopCloser = class(go.io.ReadCloser)
   571    private
   572      fReader: go.io.Reader;
   573    public
   574      constructor(aReader: go.io.Reader);
   575      begin
   576        fReader := aReader;
   577      end;
   578  
   579      method &Read(p: go.builtin.Slice<go.builtin.byte>): tuple of (go.builtin.int, go.builtin.error);
   580      begin
   581        exit fReader.Read(p);
   582      end;
   583  
   584      method Close: go.builtin.error;
   585      begin
   586        exit nil;
   587      end;
   588  
   589    end;
   590    go.io.ioutil.__Global = public partial class
   591  
   592    public
   593      class var Discard: go.io.Writer := new DiscardWriter;
   594      class method TempDir(dir, aPrefix: String): tuple of (go.builtin.string, go.builtin.error);
   595      begin
   596        {$IF ISLAND}
   597        if String.IsNullOrEmpty(dir) then
   598          dir := Environment.TempFolder.FullName;
   599        {$ELSEIF ECHOES}
   600        if String.IsNullOrEmpty(dir) then
   601          dir := Path.GetTempPath;
   602        {$ENDIF}
   603  
   604        dir := Path.Combine(dir, aPrefix+Guid.NewGuid.ToString.Replace('{','').Replace('}', '').Replace('-', ''));
   605        exit (dir, nil);
   606      end;
   607      class method WriteFile(filename: String; data: go.builtin.Slice<Byte>; per: go.os.FileMode): go.builtin.error;
   608      begin
   609        var res := OpenFile(filename, O_RDWR or O_CREATE, per); // 0755
   610        if res.Item2 <> nil then exit res.Item2;
   611        try
   612          var res2 := res.Item1.Write(data);
   613          if (res2.Item2 <> nil) then
   614            exit res2.Item2;
   615        finally
   616          res.Item1.Close;
   617        end;
   618        exit nil;
   619      end;
   620      class method ReadDir(dir: String): tuple of (go.builtin.Slice<go.os.FileInfo>, go.builtin.error);
   621      begin
   622        try
   623          exit new File(path := dir).Readdir(-1);
   624        except
   625          on e: Exception do
   626            exit (nil, go.errors.New(e.Message));
   627        end;
   628      end;
   629  
   630      class method TempFile(dir, pattern: String): tuple of (Memory<go.os.File>, go.builtin.error);
   631      begin
   632        {$IF ISLAND}
   633        if String.IsNullOrEmpty(dir) then
   634          dir := Environment.TempFolder.FullName;
   635        exit (new Memory<go.os.File>(new go.os.File(fs := new FileStream(Path.Combine(dir, pattern+Guid.NewGuid.ToString.Replace('{','').Replace('}', '').Replace('-', '')), :System.FileMode.Create, :System.FileAccess.ReadWrite))), nil);
   636        // TODO
   637        {$ELSEIF ECHOES}
   638        if String.IsNullOrEmpty(dir) then
   639          dir := Path.GetTempPath;
   640        exit (new Memory<go.os.File>(new go.os.File(fs := System.IO.File.Create(System.IO.Path.GetTempFileName))), nil);
   641        {$ENDIF}
   642      end;
   643  
   644      class method NopCloser(r: go.io.Reader): go.io.ReadCloser;
   645      begin
   646        exit new MyNopCloser(r);
   647      end;
   648  
   649      class method ReadFile(fn: String): tuple of (go.builtin.Slice<Byte>, go.builtin.error);
   650      begin
   651        var res := Open(fn);
   652        if res.Item2 <> nil then exit (nil, res.Item2);
   653        try
   654          exit ReadAll(res.Item1);
   655        finally
   656          res.Item1.Close;
   657        end;
   658      end;
   659  
   660      class method ReadAll(r: go.io.Reader): tuple of (go.builtin.Slice<Byte>, go.builtin.error);
   661      begin
   662        var ms := new MemoryStream;
   663        var b := new go.builtin.Slice<Byte>(512);
   664        loop begin
   665          var (res, i) := r.Read(b);
   666          if i <> nil then begin
   667            ms.Write(b.fArray, 0, res);
   668            if (i is go.errors.errorString) and (i.Error() = "EOF") then
   669              exit (ms.ToArray(), nil)
   670            else
   671              exit (ms.ToArray(), i);
   672          end;
   673          if res ≤ 0 then break;
   674          ms.Write(b.fArray, 0, res);
   675        end;
   676        exit(ms.ToArray(), nil);
   677      end;
   678    end;
   679  
   680  [ValueTypeSemantics]
   681  LinkError = public class(go.builtin.error)
   682  public
   683    Op: String;
   684    &Old: String;
   685    &New: String;
   686    Err: go.builtin.error;
   687    method Error(): go.builtin.string;
   688    begin
   689      exit Op + " " + &Old + " " + &New + ": " + &Err.Error()
   690    end;
   691  end;
   692  
   693  [ValueTypeSemantics]
   694  Process= public partial class
   695  public
   696    Process: ProcessType;
   697    property Pid: Integer read Process.Id;
   698    method Kill: go.builtin.error;
   699    begin
   700      try
   701        {$IF ISLAND}
   702        Process.Stop;
   703        {$ELSEIF ECHOES}
   704        Process.Kill;
   705        {$ENDIF}
   706        exit nil;
   707      except
   708        on e: Exception do
   709          exit go.errors.New(e.Message);
   710      end;
   711    end;
   712  
   713    method Release: go.builtin.error;
   714    begin
   715      {$IF ISLAND}
   716      // TODO
   717      {$ELSEIF ECHOES}
   718      Process.Dispose;
   719      {$ENDIF}
   720      exit nil;
   721    end;
   722  
   723    method Signal: go.builtin.error;
   724    begin
   725      exit go.errors.New('Not supported');
   726    end;
   727  
   728    method Wait: tuple of (Memory<ProcessState>, go.builtin.error);
   729    begin
   730      try
   731        {$IF ISLAND}
   732        Process.WaitFor;
   733        {$ELSEIF ECHOES}
   734        Process.WaitForExit;
   735        {$ENDIF}
   736        exit (Memory<ProcessState>(new ProcessState(Process)), nil);
   737      except
   738        on e: Exception do
   739          exit (nil, go.errors.New(e.Message));
   740      end;
   741    end;
   742  end;
   743  [ValueTypeSemantics]
   744  ProcAttr = public class
   745  public
   746    constructor; empty;
   747    Dir: String;
   748    Env: go.builtin.Slice<go.builtin.string>;
   749  end;
   750  
   751  method IntStartProcess(name: go.builtin.string; argv: go.builtin.Slice<go.builtin.string>; attr: Memory<ProcAttr>): tuple of (Memory<Process>, go.builtin.error);
   752  begin
   753    var lArgv := new List<String>();
   754    if argv <> nil then begin
   755      for each el in argv do
   756        lArgv.Add(el[1]);
   757    end;
   758  
   759    var lEnv := new Dictionary<String, String>();
   760    var lWorkingDir := '';
   761    if (attr <> nil) then begin
   762      var p := attr^;
   763      if p <> nil then begin
   764        if p.Dir <> nil then
   765          lWorkingDir := p.Dir;
   766        if p.Env <> nil then
   767          for each el in p.Env do begin
   768            var n := go.strings.SplitN(el[1], '=', 2);
   769            if n.Length <> 2 then continue;
   770            lEnv.Add(n[0], n[1]);
   771          end;
   772      end;
   773    end;
   774    {$IF ECHOES}
   775    var lPSI := new System.Diagnostics.ProcessStartInfo(name);
   776    for each el in lArgv do
   777      if go.builtin.string.IsNullOrEmpty(lPSI.Arguments) then begin
   778        lPSI.Arguments := '"'+ el +'"';
   779      end else begin
   780        lPSI.Arguments := lPSI.Arguments+' "' + el + '"';
   781      end;
   782  
   783    lPSI.UseShellExecute := false;
   784    lPSI.WorkingDirectory := lWorkingDir;
   785  
   786    for each el in lEnv do
   787      lPSI.EnvironmentVariables.Add(el.Key, el.Value);
   788    {$ENDIF}
   789    try
   790      {$IF ISLAND}
   791      var lProcess := new ProcessType(name, lArgv, lEnv, lWorkingDir);
   792      lProcess.RedirectOutput := true;
   793      lProcess.Start;
   794      exit (Memory<Process>(new Process(Process := lProcess)), nil);
   795      {$ELSEIF ECHOES}
   796      exit (Memory<Process>(new Process(Process := ProcessType.Start(lPSI))), nil);
   797      {$ENDIF}
   798    except
   799      on e: Exception do
   800        exit (nil, go.errors.New(e.Message));
   801    end;
   802  end;
   803  
   804  method StartProcess(name: go.builtin.string; argv: go.builtin.Slice<go.builtin.string>; attr: Memory<ProcAttr>): tuple of (Memory<Process>, go.builtin.error);
   805  begin
   806    exit IntStartProcess(name, argv, attr);
   807  end;
   808  
   809  method FindProcess(pid: Integer): tuple of (Memory<Process>, go.builtin.error);
   810  begin
   811    try
   812      {$IF ISLAND}
   813      var p: ProcessType := nil; // TODO
   814      {$ELSEIF ECHOES}
   815      var p := ProcessType.GetProcessById(pid);
   816      {$ENDIF}
   817      if p = nil then
   818        exit (nil, go.errors.New('No such process'));;
   819      exit (Memory<Process>(new Process(Process := p)), nil);
   820    except
   821      on e: Exception do
   822        exit (nil, go.errors.New(e.Message));
   823    end;
   824  end;
   825  
   826  type
   827  go.crypto.x509.__Global = public partial class
   828  {$IF ISLAND AND DARWIN AND NOT (IOS OR TVOS OR WATCHOS)}
   829    class method FetchPEMRoots(pemRoots: ^CFDataRef; untrustedPemRoots: ^CFDataRef): Integer;
   830    begin
   831      var domains: array of SecTrustSettingsDomain := [SecTrustSettingsDomain.kSecTrustSettingsDomainSystem, SecTrustSettingsDomain.kSecTrustSettingsDomainAdmin,
   832                                                       SecTrustSettingsDomain.kSecTrustSettingsDomainUser];
   833      var numDomains := sizeOf(domains) / sizeOf(SecTrustSettingsDomain);
   834      if pemRoots = nil then
   835        exit -1;
   836  
   837      var policy: CFStringRef := CFStringCreateWithCString(NULL, "kSecTrustSettingsResult", CFStringBuiltInEncodings.kCFStringEncodingUTF8);
   838      var combinedData: CFMutableDataRef := CFDataCreateMutable(kCFAllocatorDefault, 0);
   839      var combinedUntrustedData: CFMutableDataRef := CFDataCreateMutable(kCFAllocatorDefault, 0);
   840      var appendTo: CFMutableDataRef;
   841      for i: Integer := 0 to numDomains - 1 do begin
   842        var j: Integer;
   843        var certs: CFArrayRef := nil;
   844        var err: OSStatus := SecTrustSettingsCopyCertificates(domains[i], @certs);
   845        if err <> rtl.noErr then
   846          continue;
   847  
   848        var untrusted: Integer := 0;
   849        var trustAsRoot: Integer := 0;
   850        var trustRoot: Integer := 0;
   851        var numCerts: CFIndex := CFArrayGetCount(certs);
   852  
   853        for j: Integer := 0 to  numCerts - 1 do begin
   854          var data: CFDataRef := nil;
   855          var errRef: CFErrorRef := nil;
   856          var trustSettings: CFArrayRef := nil;
   857          var cert: SecCertificateRef := SecCertificateRef(CFArrayGetValueAtIndex(certs, j));
   858          if cert = nil then
   859            continue;
   860          // We only want trusted certs.
   861          untrusted := 0;
   862          trustAsRoot := 0;
   863          trustRoot := 0;
   864          if i = 0 then
   865            trustAsRoot := 1
   866          else begin
   867            var k: Integer;
   868            var m: CFIndex;
   869            // Certs found in the system domain are always trusted. If the user
   870            // configures "Never Trust" on such a cert, it will also be found in the
   871            // admin or user domain, causing it to be added to untrustedPemRoots. The
   872            // Go code will then clean this up.
   873  
   874            // Trust may be stored in any of the domains. According to Apple's
   875            // SecTrustServer.c, "user trust settings overrule admin trust settings",
   876            // so take the last trust settings array we find.
   877            // Skip the system domain since it is always trusted.
   878            for k := i to numDomains - 1 do begin
   879              var domainTrustSettings: CFArrayRef := nil;
   880              err := SecTrustSettingsCopyTrustSettings(cert, domains[k], @domainTrustSettings);
   881              if (err = errSecSuccess) and (domainTrustSettings <> nil) then begin
   882                if trustSettings ≠ nil then
   883                  CFRelease(trustSettings);
   884                trustSettings := domainTrustSettings;
   885              end;
   886            end;
   887  
   888            if trustSettings = nil then
   889              // "this certificate must be verified to a known trusted certificate"; aka not a root.
   890              continue;
   891  
   892            for m := 0 to CFArrayGetCount(trustSettings) - 1 do begin
   893              var cfNum: CFNumberRef;
   894              var tSetting: CFDictionaryRef := CFDictionaryRef(CFArrayGetValueAtIndex(trustSettings, m));
   895              if CFDictionaryGetValueIfPresent(tSetting, policy, @cfNum) then begin
   896                var lResult: SecTrustSettingsResult := SecTrustSettingsResult.kSecTrustSettingsResultUnspecified;
   897                CFNumberGetValue(cfNum, CFNumberType.kCFNumberSInt32Type, ^Void(@lResult));
   898                // TODO: The rest of the dictionary specifies conditions for evaluation.
   899                if lResult = SecTrustSettingsResult.kSecTrustSettingsResultDeny then
   900                  untrusted := 1
   901                else
   902                  if lResult = SecTrustSettingsResult.kSecTrustSettingsResultTrustAsRoot then
   903                    trustAsRoot := 1
   904                  else
   905                    if lResult = SecTrustSettingsResult.kSecTrustSettingsResultTrustRoot then
   906                      trustRoot := 1
   907              end;
   908            end;
   909            CFRelease(trustSettings);
   910          end;
   911  
   912          if trustRoot > 0 then begin
   913            // We only want to add Root CAs, so make sure Subject and Issuer Name match
   914            var subjectName: CFDataRef := SecCertificateCopyNormalizedSubjectContent(cert, @errRef);
   915            if errRef ≠ nil then begin
   916              CFRelease(errRef);
   917              continue;
   918            end;
   919            var issuerName: CFDataRef := SecCertificateCopyNormalizedIssuerContent(cert, @errRef);
   920            if errRef ≠ nil then begin
   921              CFRelease(subjectName);
   922              CFRelease(errRef);
   923              continue;
   924            end;
   925            var equal: Boolean := CFEqual(subjectName, issuerName);
   926            CFRelease(subjectName);
   927            CFRelease(issuerName);
   928            if not equal then
   929              continue;
   930          end;
   931  
   932          err := SecItemExport(cert, SecExternalFormat.kSecFormatX509Cert, SecItemImportExportFlags.kSecItemPemArmour, nil, @data);
   933          if err ≠ rtl.noErr then
   934            continue;
   935  
   936          if data ≠ nil then begin
   937            if (trustRoot = 0) and (trustAsRoot = 0) then
   938              untrusted := 1;
   939  
   940            appendTo := if untrusted = 1 then combinedUntrustedData else combinedData;
   941            CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
   942            CFRelease(data);
   943          end;
   944        end;
   945        CFRelease(certs);
   946      end;
   947  
   948      CFRelease(policy);
   949      pemRoots^ := combinedData;
   950      untrustedPemRoots^ := combinedUntrustedData;
   951      exit(0);
   952    end;
   953  
   954    class method loadSystemRoots: tuple of (Memory<go.crypto.x509.CertPool>, go.builtin.error);
   955    begin
   956      var roots := NewCertPool();
   957      var data: CFDataRef := 0;
   958      var untrustedData: CFDataRef := 0;
   959      var err := FetchPEMRoots(@data, @untrustedData);
   960      if err = -1 then
   961        exit (nil, go.errors.New("crypto/x509: failed to load darwin system roots with cgo"));
   962  
   963      var buf := CFDataGetBytePtr(data);
   964      var lTotal := CFDataGetLength(data);
   965      var lSlice := new go.builtin.Slice<go.builtin.byte>(lTotal);
   966      for i: Integer := 0 to lTotal - 1 do begin
   967        lSlice[i] := buf^;
   968        inc(buf);
   969      end;
   970  
   971      //roots.AppendCertsFromPEM(lSlice);
   972      go.crypto.x509.CertPool.AppendCertsFromPEM(roots, lSlice);
   973      if untrustedData = nil then begin
   974        CFRelease(data);
   975        exit (roots, nil);
   976      end;
   977      buf := CFDataGetBytePtr(untrustedData);
   978      lTotal := CFDataGetLength(untrustedData);
   979      var untrustedRoots := NewCertPool();
   980      lSlice := new go.builtin.Slice<go.builtin.byte>(lTotal);
   981      for i: Integer := 0 to lTotal - 1 do begin
   982        lSlice[i] := buf^;
   983        inc(buf);
   984      end;
   985      //untrustedRoots.AppendCertsFromPEM(lSlice);
   986      go.crypto.x509.CertPool.AppendCertsFromPEM(untrustedRoots, lSlice);
   987      var trustedRoots := go.crypto.x509.NewCertPool();
   988      for lCert in roots.certs do
   989        //if not untrustedRoots.contains(lCert[1]) then begin
   990        if not go.crypto.x509.CertPool.contains(untrustedRoots, lCert[1]) then begin
   991          //trustedRoots.AddCert(lCert[1]);
   992          go.crypto.x509.CertPool.AddCert(trustedRoots, lCert[1]);
   993        end;
   994  
   995      CFRelease(data);
   996      CFRelease(untrustedData);
   997      exit (trustedRoots, nil);
   998    end;
   999  {$ENDIF}
  1000  end;
  1001  
  1002  end.