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.