mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-12 19:29:28 +02:00

svn+ssh://jonas@svn.freepascal.org/FPC/svn/fpc/branches/wpo ........ r11878 | jonas | 2008-10-11 02:25:18 +0200 (Sat, 11 Oct 2008) | 19 lines + initial implementation of whole-program optimisation framework + implementation of whole-program devirtualisation o use: a) generate whole-program optimisation information (no need to completely compile the program and all of its units with -OW/-FW, only the main program is sufficient) fpc -OWdevirtcalls -FWmyprog.wpo myprog b) use it to optimise the program fpc -B -Owdevirtcalls -Fwmyprog.wpo myprog (the -B is not required, but only sources recompiled during the second pass will actually be optimised -- if you want, you can even rebuild the rtl devirtualised for a particular program; and these options can obviously also be used together with regular optimisation switches) o warning: - there are no checks yet to ensure that you do not use units optimised for a particular program with another program (or with a changed version of the same program) ........ r11881 | jonas | 2008-10-11 19:35:52 +0200 (Sat, 11 Oct 2008) | 13 lines * extracted code to detect constructed class/object types from tcallnode.gen_vmt_tree into its own method to avoid clutter * detect x.classtype.create constructs (with classtype = the system.tobject.classtype method), and treat them as if a "class of x" has been instantiated rather than a "class of tobject". this required storing the instantiated classrefs in their own array though, because at such a point we don't have a "class of x" tdef available (so now "x", and all other defs instantiated via a classref, are now stored as tobjectdefs in a separate array) + support for devirtualising class methods (including constructors) ........ r11882 | jonas | 2008-10-11 20:44:02 +0200 (Sat, 11 Oct 2008) | 7 lines + -Owoptvmts whole program optimisation which replaces vmt entries with method names of child classes in case the current class' method can never be called (e.g., because this class is never instantiated). As a result, such methods can then be removed by dead code removal/smart linking (not much effect for either the compiler, lazarus or a trivial lazarus app though). ........ r11889 | jonas | 2008-10-12 14:29:54 +0200 (Sun, 12 Oct 2008) | 2 lines * some comment fixes ........ r11891 | jonas | 2008-10-12 18:49:13 +0200 (Sun, 12 Oct 2008) | 4 lines * fixed twpofilereader.getnextnoncommentline() when reusing a previously read line * fixed skipping of unnecessary wpo feedback file sections ........ r11892 | jonas | 2008-10-12 23:42:43 +0200 (Sun, 12 Oct 2008) | 31 lines + symbol liveness wpo information extracted from smartlinked programs (-OW/-Owsymbolliveness) + use symbol liveness information to improve devirtualisation (don't consider classes created in code that has been dead code stripped). This requires at least two passes of using wpo (first uses dead code info to locate classes that are constructed only in dead code, second pass uses this info to potentially further devirtualise). I.e.: 1) generate initial liveness and devirtualisation feedback fpc -FWtt.wpo -OWall tt.pp -Xs- -CX -XX 2) use previously generated feedback, and regenerate new feedback based on this (i.e., disregard classes created in dead code) fpc -FWtt-1.wpo -OWall -Fwtt.wo -Owall tt.pp -Xs- -CX -XX 3) use the newly generated feedback (in theory, it is possible that even more opportunities pop up afterwards; you can continue until the program does not get smaller anymore) fpc -Fwtt-1.wpo -Owall tt.pp -CX -XX * changed all message() to cgmessage() calls so the set codegenerror * changed static fsectionhandlers field to a regular field called fwpocomponents * changed registration of wpocomponents: no longer happens in the initialization section of their unit, but in the InitWpo routine (which has been moved from the woinfo to the wpo unit). This way you can register different classes based on the target/parameters. + added static method to twpocomponentbase for checking whether the command line parameters don't conflict with the requested optimisations (e.g. generating liveness info requires that smartlinking is turned on) + added static method to twpocomponentbase to request the section name ........ r11893 | jonas | 2008-10-12 23:53:57 +0200 (Sun, 12 Oct 2008) | 3 lines * fixed comment error (twpodeadcodeinfo keeps a list of live, not dead symbols) ........ r11895 | jonas | 2008-10-13 00:13:59 +0200 (Mon, 13 Oct 2008) | 2 lines + documented -OW<x>, -Ow<x>, -FW<x> and -Fw<x> wpo parameters ........ r11899 | jonas | 2008-10-14 22:14:56 +0200 (Tue, 14 Oct 2008) | 2 lines * replaced hardcoded string with objdumpsearchstr constant ........ r11900 | jonas | 2008-10-14 22:15:25 +0200 (Tue, 14 Oct 2008) | 2 lines * reset wpofeedbackinput and wpofeedbackoutput in wpodone ........ r11901 | jonas | 2008-10-14 22:16:07 +0200 (Tue, 14 Oct 2008) | 2 lines * various additional comments and comment fixes ........ r11902 | jonas | 2008-10-15 18:09:42 +0200 (Wed, 15 Oct 2008) | 5 lines * store vmt procdefs in the ppu files so we don't have to use a hack to regenerate them for whole-program optimisation * fixed crash when performing devirtualisation optimisation on programs that do not construct any classes/objects with optimisable vmts ........ r11935 | jonas | 2008-10-19 12:24:26 +0200 (Sun, 19 Oct 2008) | 4 lines * set the vmt entries of non-class virtual methods of not instantiated objects/classes to FPC_ABSTRACTERROR so the code they refer to can be thrown away if it is not referred to in any other way either ........ r11938 | jonas | 2008-10-19 20:55:02 +0200 (Sun, 19 Oct 2008) | 7 lines * record all classrefdefs/objdefs for which a loadvmtaddrnode is generated, and instead of marking all classes that derive from instantiated classrefdefs as instantiated, only mark those classes from the above collection that derive from instantiated classrefdefs as instantiated (since to instantiate a class, you have to load its vmt somehow -- this may be broken by using assembler code though) ........ r12212 | jonas | 2008-11-23 12:26:34 +0100 (Sun, 23 Nov 2008) | 3 lines * fixed to work with the new vmtentries that are always available and removed previously added code to save/load vmtentries to ppu files ........ r12304 | jonas | 2008-12-05 22:23:30 +0100 (Fri, 05 Dec 2008) | 4 lines * check whether the correct wpo feedback file is used in the current compilation when using units that were compiled using wpo information during a previous compilation run ........ r12308 | jonas | 2008-12-06 18:03:39 +0100 (Sat, 06 Dec 2008) | 2 lines * abort compilation if an error occurred during wpo initialisation ........ r12309 | jonas | 2008-12-06 18:04:28 +0100 (Sat, 06 Dec 2008) | 3 lines * give an error message instead of crashing with an io exception if the compiler is unable to create the wpo feedback file specified using -FW ........ r12310 | jonas | 2008-12-06 18:12:43 +0100 (Sat, 06 Dec 2008) | 3 lines * don't let the used wpo feedback file influence the interface crc (there's a separate check for such changes) ........ r12316 | jonas | 2008-12-08 19:08:25 +0100 (Mon, 08 Dec 2008) | 3 lines * document the format of the sections of the wpo feedback file inside the feedback file itself ........ r12330 | jonas | 2008-12-10 22:26:47 +0100 (Wed, 10 Dec 2008) | 2 lines * use sysutils instead of dos to avoid command line length limits ........ r12331 | jonas | 2008-12-10 22:31:11 +0100 (Wed, 10 Dec 2008) | 3 lines + support for testing whole program optimisation tests (multiple compilations using successively generated feedback files) ........ r12332 | jonas | 2008-12-10 22:31:40 +0100 (Wed, 10 Dec 2008) | 2 lines + whole program optimisation tests ........ r12334 | jonas | 2008-12-10 22:38:07 +0100 (Wed, 10 Dec 2008) | 2 lines - removed unused local variable ........ r12339 | jonas | 2008-12-11 18:06:36 +0100 (Thu, 11 Dec 2008) | 2 lines + comments for newly added fields to tobjectdef for devirtualisation ........ r12340 | jonas | 2008-12-11 18:10:01 +0100 (Thu, 11 Dec 2008) | 2 lines * increase ppu version (was no longer different from trunk due to merging) ........ git-svn-id: trunk@12341 -
1084 lines
25 KiB
ObjectPascal
1084 lines
25 KiB
ObjectPascal
{
|
|
This file is part of the Free Pascal Test Suite
|
|
Copyright (c) 1999-2000 by Pierre Muller
|
|
|
|
Unit to redirect output and error to files
|
|
|
|
Adapted from code donated to public domain by Schwartz Gabriel 20/03/1993
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
**********************************************************************}
|
|
Unit Redir;
|
|
Interface
|
|
|
|
{$H+}
|
|
{$R-}
|
|
{$ifndef Linux}
|
|
{$ifndef Unix}
|
|
{$S-}
|
|
{$endif}
|
|
{$endif}
|
|
|
|
{$ifdef TP}
|
|
{$define implemented}
|
|
{$endif TP}
|
|
{$ifdef Go32v2}
|
|
{$define implemented}
|
|
{$endif}
|
|
{$ifdef OS2}
|
|
{$define shell_implemented}
|
|
{$endif}
|
|
{$ifdef windows}
|
|
{$define implemented}
|
|
{$endif}
|
|
{$ifdef linux}
|
|
{$define implemented}
|
|
{$endif}
|
|
{$ifdef BSD}
|
|
{$define implemented}
|
|
{$endif}
|
|
{$ifdef BEOS}
|
|
{$define implemented}
|
|
{$endif}
|
|
{$ifdef macos}
|
|
{$define shell_implemented}
|
|
{$endif}
|
|
{$ifdef sunos}
|
|
{$define implemented}
|
|
{$endif}
|
|
|
|
{ be sure msdos is not set for FPC compiler }
|
|
{$ifdef FPC}
|
|
{$UnDef MsDos}
|
|
{$endif FPC}
|
|
|
|
Var
|
|
IOStatus : Integer;
|
|
RedirErrorOut,RedirErrorIn,
|
|
RedirErrorError : Integer;
|
|
ExecuteResult : Longint;
|
|
|
|
{------------------------------------------------------------------------------}
|
|
procedure InitRedir;
|
|
function ExecuteRedir (Const ProgName, ComLine : String; RedirStdIn, RedirStdOut, RedirStdErr: String): boolean;
|
|
procedure DosExecute(ProgName, ComLine : String);
|
|
|
|
function ChangeRedirOut(Const Redir : String; AppendToFile : Boolean) : Boolean;
|
|
procedure RestoreRedirOut;
|
|
procedure DisableRedirOut;
|
|
procedure EnableRedirOut;
|
|
function ChangeRedirIn(Const Redir : String) : Boolean;
|
|
procedure RestoreRedirIn;
|
|
procedure DisableRedirIn;
|
|
procedure EnableRedirIn;
|
|
function ChangeRedirError(Const Redir : String; AppendToFile : Boolean) : Boolean;
|
|
procedure RestoreRedirError;
|
|
procedure DisableRedirError;
|
|
procedure EnableRedirError;
|
|
procedure RedirDisableAll;
|
|
procedure RedirEnableAll;
|
|
|
|
{ unused in UNIX }
|
|
const
|
|
UseComSpec : boolean = true;
|
|
|
|
Implementation
|
|
|
|
{$ifdef macos}
|
|
{$define usedos}
|
|
{$endif}
|
|
|
|
Uses
|
|
{$ifdef go32v2}
|
|
go32,
|
|
{$endif go32v2}
|
|
{$ifdef windows}
|
|
windows,
|
|
{$endif windows}
|
|
{$ifdef unix}
|
|
{$ifdef ver1_0}
|
|
linux,
|
|
{$else}
|
|
baseunix,
|
|
unix,
|
|
{$endif}
|
|
{$endif unix}
|
|
{$ifdef usedos}
|
|
dos;
|
|
{$else}
|
|
sysutils;
|
|
{$endif}
|
|
|
|
Const
|
|
{$ifdef UNIX}
|
|
DirSep='/';
|
|
listsep = [';',':'];
|
|
exeext = '';
|
|
{$else UNIX}
|
|
{$ifdef MACOS}
|
|
DirSep=':';
|
|
listsep = [','];
|
|
exeext = '';
|
|
{$else MACOS}
|
|
DirSep='\';
|
|
listsep = [';'];
|
|
exeext = '.exe';
|
|
{$endif MACOS}
|
|
{$endif UNIX}
|
|
|
|
{$ifndef usedos}
|
|
{ code from: }
|
|
{ Lithuanian Text Tool version 0.9.0 (2001-04-19) }
|
|
{ Copyright (c) 1999-2001 Marius Gedminas <mgedmin@delfi.lt> }
|
|
{ (GPLv2 or later) }
|
|
|
|
function FExpand(const S: string): string;
|
|
begin
|
|
FExpand := ExpandFileName(S);
|
|
end;
|
|
|
|
type
|
|
PathStr = string;
|
|
DirStr = string;
|
|
NameStr = string;
|
|
ExtStr = string;
|
|
|
|
procedure FSplit(Path: PathStr; var Dir: DirStr; var Name: NameStr; var Ext: ExtStr);
|
|
begin
|
|
Dir := ExtractFilePath(Path);
|
|
Name := ChangeFileExt(ExtractFileName(Path), '');
|
|
Ext := ExtractFileExt(Path);
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
var
|
|
FIN,FOUT,FERR : ^File;
|
|
RedirStdErrToStdOut,
|
|
RedirChangedOut,
|
|
RedirChangedIn : Boolean;
|
|
RedirChangedError : Boolean;
|
|
InRedirDisabled,OutRedirDisabled,ErrorRedirDisabled : Boolean;
|
|
|
|
|
|
{*****************************************************************************
|
|
Helpers
|
|
*****************************************************************************}
|
|
|
|
function FixPath(const s:string):string;
|
|
var
|
|
i : longint;
|
|
begin
|
|
{ Fix separator }
|
|
setlength(fixpath,length(s));
|
|
for i:=1 to length(s) do
|
|
if s[i] in ['/','\'] then
|
|
fixpath[i]:=DirSep
|
|
else
|
|
fixpath[i]:=s[i];
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
Dos
|
|
*****************************************************************************}
|
|
|
|
{$ifdef implemented}
|
|
|
|
{$ifdef TP}
|
|
|
|
{$ifndef windows}
|
|
const
|
|
UnusedHandle = -1;
|
|
StdInputHandle = 0;
|
|
StdOutputHandle = 1;
|
|
StdErrorHandle = 2;
|
|
{$endif windows}
|
|
|
|
Type
|
|
PtrRec = packed record
|
|
Ofs, Seg : Word;
|
|
end;
|
|
|
|
PHandles = ^THandles;
|
|
THandles = Array [Byte] of Byte;
|
|
|
|
PWord = ^Word;
|
|
|
|
Var
|
|
MinBlockSize : Word;
|
|
MyBlockSize : Word;
|
|
Handles : PHandles;
|
|
PrefSeg : Word;
|
|
OldHandleOut,OldHandleIn,OldHandleError : Byte;
|
|
{$endif TP}
|
|
|
|
var
|
|
TempHOut, TempHIn,TempHError : longint;
|
|
|
|
{
|
|
For linux the following functions exist
|
|
Function fpdup(oldfile:longint;var newfile:longint):Boolean;
|
|
Function fpdup2(oldfile,newfile:longint):Boolean;
|
|
Function fpClose(fd:longint):boolean;
|
|
}
|
|
{$ifdef go32v2}
|
|
|
|
function dup(fh : longint;var nh : longint) : boolean;
|
|
var
|
|
Regs : Registers;
|
|
begin
|
|
Regs.ah:=$45;
|
|
Regs.bx:=fh;
|
|
MsDos (Regs);
|
|
dup:=true;
|
|
If (Regs.Flags and fCarry)=0 then
|
|
nh:=Regs.Ax
|
|
else
|
|
dup:=false;
|
|
end;
|
|
|
|
function dup2(fh,nh : longint) : boolean;
|
|
var
|
|
Regs : Registers;
|
|
begin
|
|
dup2:=true;
|
|
If fh=nh then
|
|
exit;
|
|
Regs.ah:=$46;
|
|
Regs.bx:=fh;
|
|
Regs.cx:=nh;
|
|
MsDos (Regs);
|
|
If (Regs.Flags and fCarry)<>0 then
|
|
dup2:=false;
|
|
end;
|
|
|
|
{$ifndef ver1_0}
|
|
function fpdup(fh:longint):longint;
|
|
begin
|
|
if not dup(fh,fpdup) then
|
|
fpdup:=-1;
|
|
end;
|
|
|
|
function fpdup2(fh,nh:longint):longint;
|
|
begin
|
|
if dup2(fh,nh) then
|
|
fpdup2:=0
|
|
else
|
|
fpdup2:=-1;
|
|
end;
|
|
{$endif ver1_0}
|
|
|
|
|
|
Function {$ifdef ver1_0}fdclose{$else}fpclose{$endif} (Handle : Longint) : boolean;
|
|
var Regs: registers;
|
|
begin
|
|
Regs.Eax := $3e00;
|
|
Regs.Ebx := Handle;
|
|
MsDos(Regs);
|
|
{$ifdef ver1_0}fdclose{$else}fpclose{$endif}:=(Regs.Flags and fCarry)=0;
|
|
end;
|
|
|
|
{$endif def go32v2}
|
|
|
|
{$ifdef windows}
|
|
Function {$ifdef ver1_0}fdclose{$else}fpclose{$endif} (Handle : Longint) : boolean;
|
|
begin
|
|
{ Do we need this ?? }
|
|
{$ifdef ver1_0}fdclose{$else}fpclose{$endif}:=true;
|
|
end;
|
|
{$endif}
|
|
|
|
{$ifdef os2}
|
|
Function {$ifdef ver1_0}fdclose{$else}fpclose{$endif} (Handle : Longint) : boolean;
|
|
begin
|
|
{ Do we need this ?? }
|
|
{$ifdef ver1_0}fdclose{$else}fpclose{$endif}:=true;
|
|
end;
|
|
{$endif}
|
|
|
|
{$ifdef TP}
|
|
Function {$ifdef ver1_0}fdclose{$else}fpclose{$endif} (Handle : Longint) : boolean;
|
|
begin
|
|
{ if executed as under GO32 this hangs the DOS-prompt }
|
|
{$ifdef ver1_0}fdclose{$else}fpclose{$endif}:=true;
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
{$I-}
|
|
function FileExist(const FileName : PathStr) : Boolean;
|
|
{$ifdef usedos}
|
|
var
|
|
f : file;
|
|
Attr : word;
|
|
{$endif}
|
|
begin
|
|
{$ifdef usedos}
|
|
Assign(f, FileName);
|
|
GetFAttr(f, Attr);
|
|
FileExist := DosError = 0;
|
|
{$else}
|
|
FileExist := Sysutils.FileExists(filename);
|
|
{$endif}
|
|
end;
|
|
|
|
function CompleteDir(const Path: string): string;
|
|
begin
|
|
{ keep c: untouched PM }
|
|
if (Path<>'') and (Path[Length(Path)]<>DirSep) and
|
|
(Path[Length(Path)]<>':') then
|
|
CompleteDir:=Path+DirSep
|
|
else
|
|
CompleteDir:=Path;
|
|
end;
|
|
|
|
|
|
function LocateExeFile(var FileName:string): boolean;
|
|
var
|
|
dir,s,d,n,e : string;
|
|
i : longint;
|
|
begin
|
|
LocateExeFile:=False;
|
|
if FileExist(FileName) then
|
|
begin
|
|
LocateExeFile:=true;
|
|
Exit;
|
|
end;
|
|
|
|
Fsplit(Filename,d,n,e);
|
|
|
|
if (e='') and FileExist(FileName+exeext) then
|
|
begin
|
|
FileName:=FileName+exeext;
|
|
LocateExeFile:=true;
|
|
Exit;
|
|
end;
|
|
|
|
{$ifdef usedos}
|
|
S:=GetEnv('PATH');
|
|
{$else}
|
|
S:=GetEnvironmentVariable('PATH');
|
|
{$endif}
|
|
While Length(S)>0 do
|
|
begin
|
|
i:=1;
|
|
While (i<=Length(S)) and not (S[i] in ListSep) do
|
|
Inc(i);
|
|
Dir:=CompleteDir(Copy(S,1,i-1));
|
|
if i<Length(S) then
|
|
Delete(S,1,i)
|
|
else
|
|
S:='';
|
|
if FileExist(Dir+FileName) then
|
|
Begin
|
|
FileName:=Dir+FileName;
|
|
LocateExeFile:=true;
|
|
Exit;
|
|
End;
|
|
end;
|
|
end;
|
|
|
|
|
|
{............................................................................}
|
|
|
|
function ChangeRedirOut(Const Redir : String; AppendToFile : Boolean) : Boolean;
|
|
begin
|
|
ChangeRedirOut:=False;
|
|
If Redir = '' then Exit;
|
|
Assign (FOUT^, Redir);
|
|
If AppendToFile and FileExist(Redir) then
|
|
Begin
|
|
Reset(FOUT^,1);
|
|
Seek(FOUT^,FileSize(FOUT^));
|
|
End else Rewrite (FOUT^);
|
|
|
|
RedirErrorOut:=IOResult;
|
|
IOStatus:=RedirErrorOut;
|
|
If IOStatus <> 0 then Exit;
|
|
{$ifndef FPC}
|
|
Handles:=Ptr (prefseg, PWord (Ptr (prefseg, $34))^);
|
|
OldHandleOut:=Handles^[StdOutputHandle];
|
|
Handles^[StdOutputHandle]:=Handles^[FileRec (FOUT^).Handle];
|
|
ChangeRedirOut:=True;
|
|
OutRedirDisabled:=False;
|
|
{$else}
|
|
{$ifdef windows}
|
|
if SetStdHandle(Std_Output_Handle,FileRec(FOUT^).Handle) then
|
|
{$else not windows}
|
|
{$ifdef ver1_0}
|
|
dup(StdOutputHandle,TempHOut);
|
|
dup2(FileRec(FOUT^).Handle,StdOutputHandle);
|
|
{$else}
|
|
TempHOut:=fpdup(StdOutputHandle);
|
|
fpdup2(FileRec(FOUT^).Handle,StdOutputHandle);
|
|
{$endif}
|
|
if (TempHOut<>UnusedHandle) and
|
|
(StdOutputHandle<>UnusedHandle) then
|
|
{$endif not windows}
|
|
begin
|
|
ChangeRedirOut:=True;
|
|
OutRedirDisabled:=False;
|
|
end;
|
|
{$endif def FPC}
|
|
RedirChangedOut:=True;
|
|
end;
|
|
|
|
function ChangeRedirIn(Const Redir : String) : Boolean;
|
|
begin
|
|
ChangeRedirIn:=False;
|
|
If Redir = '' then Exit;
|
|
Assign (FIN^, Redir);
|
|
Reset(FIN^,1);
|
|
|
|
RedirErrorIn:=IOResult;
|
|
IOStatus:=RedirErrorIn;
|
|
If IOStatus <> 0 then Exit;
|
|
{$ifndef FPC}
|
|
Handles:=Ptr (prefseg, PWord (Ptr (prefseg, $34))^);
|
|
OldHandleIn:=Handles^[StdInputHandle];
|
|
Handles^[StdInputHandle]:=Handles^[FileRec (FIN^).Handle];
|
|
ChangeRedirIn:=True;
|
|
InRedirDisabled:=False;
|
|
{$else}
|
|
{$ifdef windows}
|
|
if SetStdHandle(Std_Input_Handle,FileRec(FIN^).Handle) then
|
|
{$else not windows}
|
|
{$ifdef ver1_0}
|
|
dup(StdInputHandle,TempHIn);
|
|
dup2(FileRec(FIn^).Handle,StdInputHandle);
|
|
{$else}
|
|
TempHIn:=fpdup(StdInputHandle);
|
|
fpdup2(FileRec(FIn^).Handle,StdInputHandle);
|
|
{$endif}
|
|
if (TempHIn<>UnusedHandle) and
|
|
(StdInputHandle<>UnusedHandle) then
|
|
{$endif not windows}
|
|
begin
|
|
ChangeRedirIn:=True;
|
|
InRedirDisabled:=False;
|
|
end;
|
|
{$endif def FPC}
|
|
RedirChangedIn:=True;
|
|
end;
|
|
|
|
|
|
function ChangeRedirError(Const Redir : String; AppendToFile : Boolean) : Boolean;
|
|
var
|
|
PF : ^File;
|
|
begin
|
|
ChangeRedirError:=False;
|
|
If Redir = '' then
|
|
Exit;
|
|
RedirStdErrToStdOut:=(Redir='stdout');
|
|
if RedirStdErrToStdOut then
|
|
begin
|
|
PF:=FOut;
|
|
end
|
|
else
|
|
begin
|
|
Assign (FERR^, Redir);
|
|
If AppendToFile and FileExist(Redir) then
|
|
Begin
|
|
Reset(FERR^,1);
|
|
Seek(FERR^,FileSize(FERR^));
|
|
End
|
|
else
|
|
Rewrite (FERR^);
|
|
|
|
RedirErrorError:=IOResult;
|
|
IOStatus:=RedirErrorError;
|
|
If IOStatus <> 0 then Exit;
|
|
PF:=FErr;
|
|
end;
|
|
|
|
{$ifndef FPC}
|
|
Handles:=Ptr (prefseg, PWord (Ptr (prefseg, $34))^);
|
|
OldHandleError:=Handles^[StdErrorHandle];
|
|
Handles^[StdErrorHandle]:=Handles^[FileRec (PF^).Handle];
|
|
ChangeRedirError:=True;
|
|
ErrorRedirDisabled:=False;
|
|
{$else}
|
|
{$ifdef windows}
|
|
if SetStdHandle(Std_Error_Handle,FileRec(PF^).Handle) then
|
|
{$else not windows}
|
|
{$ifdef ver1_0}
|
|
dup(StdErrorHandle,TempHError);
|
|
dup2(FileRec(PF^).Handle,StdErrorHandle);
|
|
{$else}
|
|
TempHError:=fpdup(StdErrorHandle);
|
|
fpdup2(FileRec(PF^).Handle,StdErrorHandle);
|
|
{$endif}
|
|
if (TempHError<>UnusedHandle) and
|
|
(StdErrorHandle<>UnusedHandle) then
|
|
{$endif not windows}
|
|
begin
|
|
ChangeRedirError:=True;
|
|
ErrorRedirDisabled:=False;
|
|
end;
|
|
{$endif}
|
|
RedirChangedError:=True;
|
|
end;
|
|
|
|
|
|
{$IfDef MsDos}
|
|
{Set HeapEnd Pointer to Current Used Heapsize}
|
|
Procedure SmallHeap;assembler;
|
|
asm
|
|
mov bx,word ptr HeapPtr
|
|
shr bx,4
|
|
inc bx
|
|
add bx,word ptr HeapPtr+2
|
|
mov ax,PrefixSeg
|
|
sub bx,ax
|
|
mov es,ax
|
|
mov ah,4ah
|
|
int 21h
|
|
end;
|
|
|
|
|
|
|
|
{Set HeapEnd Pointer to Full Heapsize}
|
|
Procedure FullHeap;assembler;
|
|
asm
|
|
mov bx,word ptr HeapEnd
|
|
shr bx,4
|
|
inc bx
|
|
add bx,word ptr HeapEnd+2
|
|
mov ax,PrefixSeg
|
|
sub bx,ax
|
|
mov es,ax
|
|
mov ah,4ah
|
|
int 21h
|
|
end;
|
|
|
|
{$EndIf MsDos}
|
|
|
|
|
|
procedure RestoreRedirOut;
|
|
|
|
begin
|
|
If not RedirChangedOut then Exit;
|
|
{$ifndef FPC}
|
|
Handles^[StdOutputHandle]:=OldHandleOut;
|
|
OldHandleOut:=StdOutputHandle;
|
|
{$else}
|
|
{$ifdef windows}
|
|
SetStdHandle(Std_Output_Handle,StdOutputHandle);
|
|
{$else not windows}
|
|
{$ifdef ver1_0}dup2{$else}fpdup2{$endif}(TempHOut,StdOutputHandle);
|
|
{$endif not windows}
|
|
{$endif FPC}
|
|
Close (FOUT^);
|
|
{$ifdef ver1_0}fdclose{$else}fpclose{$endif}(TempHOut);
|
|
RedirChangedOut:=false;
|
|
end;
|
|
|
|
{............................................................................}
|
|
|
|
procedure RestoreRedirIn;
|
|
|
|
begin
|
|
If not RedirChangedIn then Exit;
|
|
{$ifndef FPC}
|
|
Handles^[StdInputHandle]:=OldHandleIn;
|
|
OldHandleIn:=StdInputHandle;
|
|
{$else}
|
|
{$ifdef windows}
|
|
SetStdHandle(Std_Input_Handle,StdInputHandle);
|
|
{$else not windows}
|
|
{$ifdef ver1_0}dup2{$else}fpdup2{$endif}(TempHIn,StdInputHandle);
|
|
{$endif not windows}
|
|
{$endif}
|
|
Close (FIn^);
|
|
{$ifdef ver1_0}fdclose{$else}fpclose{$endif}(TempHIn);
|
|
RedirChangedIn:=false;
|
|
end;
|
|
|
|
{............................................................................}
|
|
|
|
procedure DisableRedirIn;
|
|
|
|
begin
|
|
If not RedirChangedIn then Exit;
|
|
If InRedirDisabled then Exit;
|
|
{$ifndef FPC}
|
|
Handles^[StdInputHandle]:=OldHandleIn;
|
|
{$else}
|
|
{$ifdef windows}
|
|
SetStdHandle(Std_Input_Handle,StdInputHandle);
|
|
{$else not windows}
|
|
{$ifdef ver1_0}dup2{$else}fpdup2{$endif}(TempHIn,StdInputHandle);
|
|
{$endif not windows}
|
|
{$endif}
|
|
InRedirDisabled:=True;
|
|
end;
|
|
|
|
{............................................................................}
|
|
|
|
procedure EnableRedirIn;
|
|
|
|
begin
|
|
If not RedirChangedIn then Exit;
|
|
If not InRedirDisabled then Exit;
|
|
{$ifndef FPC}
|
|
Handles:=Ptr (prefseg, PWord (Ptr (prefseg, $34))^);
|
|
Handles^[StdInputHandle]:=Handles^[FileRec (FIn^).Handle];
|
|
{$else}
|
|
{$ifdef windows}
|
|
SetStdHandle(Std_Input_Handle,FileRec(FIn^).Handle);
|
|
{$else not windows}
|
|
{$ifdef ver1_0}dup2{$else}fpdup2{$endif}(FileRec(FIn^).Handle,StdInputHandle);
|
|
{$endif not windows}
|
|
{$endif}
|
|
InRedirDisabled:=False;
|
|
end;
|
|
|
|
{............................................................................}
|
|
|
|
procedure DisableRedirOut;
|
|
|
|
begin
|
|
If not RedirChangedOut then Exit;
|
|
If OutRedirDisabled then Exit;
|
|
{$ifndef FPC}
|
|
Handles^[StdOutputHandle]:=OldHandleOut;
|
|
{$else}
|
|
{$ifdef windows}
|
|
SetStdHandle(Std_Output_Handle,StdOutputHandle);
|
|
{$else not windows}
|
|
{$ifdef ver1_0}dup2{$else}fpdup2{$endif}(TempHOut,StdOutputHandle);
|
|
{$endif not windows}
|
|
{$endif}
|
|
OutRedirDisabled:=True;
|
|
end;
|
|
|
|
{............................................................................}
|
|
|
|
procedure EnableRedirOut;
|
|
|
|
begin
|
|
If not RedirChangedOut then Exit;
|
|
If not OutRedirDisabled then Exit;
|
|
{$ifndef FPC}
|
|
Handles:=Ptr (prefseg, PWord (Ptr (prefseg, $34))^);
|
|
Handles^[StdOutputHandle]:=Handles^[FileRec (FOut^).Handle];
|
|
{$else}
|
|
{$ifdef windows}
|
|
SetStdHandle(Std_Output_Handle,FileRec(FOut^).Handle);
|
|
{$else not windows}
|
|
{$ifdef ver1_0}dup2{$else}fpdup2{$endif}(FileRec(FOut^).Handle,StdOutputHandle);
|
|
{$endif not windows}
|
|
{$endif}
|
|
OutRedirDisabled:=False;
|
|
end;
|
|
|
|
{............................................................................}
|
|
|
|
procedure RestoreRedirError;
|
|
|
|
begin
|
|
If not RedirChangedError then Exit;
|
|
{$ifndef FPC}
|
|
Handles^[StdErrorHandle]:=OldHandleError;
|
|
OldHandleError:=StdErrorHandle;
|
|
{$else}
|
|
{$ifdef windows}
|
|
SetStdHandle(Std_Error_Handle,StdErrorHandle);
|
|
{$else not windows}
|
|
{$ifdef ver1_0}dup2{$else}fpdup2{$endif}(TempHError,StdErrorHandle);
|
|
{$endif not windows}
|
|
{$endif}
|
|
{ don't close when redirected to STDOUT }
|
|
if not RedirStdErrToStdOut then
|
|
Close (FERR^);
|
|
{$ifdef ver1_0}fdclose{$else}fpclose{$endif}(TempHError);
|
|
RedirChangedError:=false;
|
|
end;
|
|
|
|
{............................................................................}
|
|
|
|
procedure DisableRedirError;
|
|
|
|
begin
|
|
If not RedirChangedError then Exit;
|
|
If ErrorRedirDisabled then Exit;
|
|
{$ifndef FPC}
|
|
Handles^[StdErrorHandle]:=OldHandleError;
|
|
{$else}
|
|
{$ifdef windows}
|
|
SetStdHandle(Std_Error_Handle,StdErrorHandle);
|
|
{$else not windows}
|
|
{$ifdef ver1_0}dup2{$else}fpdup2{$endif}(TempHError,StdErrorHandle);
|
|
{$endif not windows}
|
|
{$endif}
|
|
ErrorRedirDisabled:=True;
|
|
end;
|
|
|
|
{............................................................................}
|
|
|
|
procedure EnableRedirError;
|
|
|
|
begin
|
|
If not RedirChangedError then Exit;
|
|
If not ErrorRedirDisabled then Exit;
|
|
{$ifndef FPC}
|
|
Handles:=Ptr (prefseg, PWord (Ptr (prefseg, $34))^);
|
|
Handles^[StdErrorHandle]:=Handles^[FileRec (FErr^).Handle];
|
|
{$else}
|
|
{$ifdef windows}
|
|
SetStdHandle(Std_Error_Handle,FileRec(FErr^).Handle);
|
|
{$else not windows}
|
|
{$ifdef ver1_0}dup2{$else}fpdup2{$endif}(FileRec(FERR^).Handle,StdErrorHandle);
|
|
{$endif not windows}
|
|
{$endif}
|
|
ErrorRedirDisabled:=False;
|
|
end;
|
|
|
|
{............................................................................}
|
|
|
|
function ExecuteRedir (Const ProgName, ComLine : String; RedirStdIn, RedirStdOut, RedirStdErr: String): boolean;
|
|
Begin
|
|
RedirErrorOut:=0; RedirErrorIn:=0; RedirErrorError:=0;
|
|
ExecuteResult:=0;
|
|
IOStatus:=0;
|
|
if RedirStdIn<>'' then
|
|
ChangeRedirIn(RedirStdIn);
|
|
if RedirStdOut<>'' then
|
|
ChangeRedirOut(RedirStdOut,false);
|
|
if RedirStdErr<>'stderr' then
|
|
ChangeRedirError(RedirStdErr,false);
|
|
DosExecute(ProgName,ComLine);
|
|
RestoreRedirOut;
|
|
RestoreRedirIn;
|
|
RestoreRedirError;
|
|
ExecuteRedir:=(IOStatus=0) and (RedirErrorOut=0) and
|
|
(RedirErrorIn=0) and (RedirErrorError=0) and
|
|
(ExecuteResult=0);
|
|
End;
|
|
|
|
{............................................................................}
|
|
|
|
procedure RedirDisableAll;
|
|
begin
|
|
If RedirChangedIn and not InRedirDisabled then
|
|
DisableRedirIn;
|
|
If RedirChangedOut and not OutRedirDisabled then
|
|
DisableRedirOut;
|
|
If RedirChangedError and not ErrorRedirDisabled then
|
|
DisableRedirError;
|
|
end;
|
|
|
|
{............................................................................}
|
|
|
|
procedure RedirEnableAll;
|
|
begin
|
|
If RedirChangedIn and InRedirDisabled then
|
|
EnableRedirIn;
|
|
If RedirChangedOut and OutRedirDisabled then
|
|
EnableRedirOut;
|
|
If RedirChangedError and ErrorRedirDisabled then
|
|
EnableRedirError;
|
|
end;
|
|
|
|
|
|
procedure InitRedir;
|
|
begin
|
|
{$ifndef FPC}
|
|
PrefSeg:=PrefixSeg;
|
|
{$endif FPC}
|
|
end;
|
|
|
|
{$else not implemented}
|
|
|
|
|
|
{*****************************************************************************
|
|
Fake
|
|
*****************************************************************************}
|
|
|
|
{$IFDEF SHELL_IMPLEMENTED}
|
|
{$I-}
|
|
function FileExist(const FileName : PathStr) : Boolean;
|
|
var
|
|
f : file;
|
|
Attr : word;
|
|
begin
|
|
Assign(f, FileName);
|
|
GetFAttr(f, Attr);
|
|
FileExist := DosError = 0;
|
|
end;
|
|
|
|
function CompleteDir(const Path: string): string;
|
|
begin
|
|
{ keep c: untouched PM }
|
|
if (Path<>'') and (Path[Length(Path)]<>DirSep) and
|
|
(Path[Length(Path)]<>':') then
|
|
CompleteDir:=Path+DirSep
|
|
else
|
|
CompleteDir:=Path;
|
|
end;
|
|
|
|
|
|
function LocateExeFile(var FileName:string): boolean;
|
|
var
|
|
dir,s,d,n,e : string;
|
|
i : longint;
|
|
begin
|
|
LocateExeFile:=False;
|
|
if FileExist(FileName) then
|
|
begin
|
|
LocateExeFile:=true;
|
|
Exit;
|
|
end;
|
|
|
|
Fsplit(Filename,d,n,e);
|
|
|
|
if (e='') and FileExist(FileName+exeext) then
|
|
begin
|
|
FileName:=FileName+exeext;
|
|
LocateExeFile:=true;
|
|
Exit;
|
|
end;
|
|
{$ifdef macos}
|
|
S:=GetEnv('Commands');
|
|
{$else}
|
|
S:=GetEnv('PATH');
|
|
{$endif}
|
|
While Length(S)>0 do
|
|
begin
|
|
i:=1;
|
|
While (i<=Length(S)) and not (S[i] in ListSep) do
|
|
Inc(i);
|
|
Dir:=CompleteDir(Copy(S,1,i-1));
|
|
if i<Length(S) then
|
|
Delete(S,1,i)
|
|
else
|
|
S:='';
|
|
if FileExist(Dir+FileName) then
|
|
Begin
|
|
FileName:=Dir+FileName;
|
|
LocateExeFile:=true;
|
|
Exit;
|
|
End;
|
|
end;
|
|
end;
|
|
|
|
function ExecuteRedir (Const ProgName, ComLine : String; RedirStdIn, RedirStdOut, RedirStdErr: String): boolean;
|
|
var
|
|
CmdLine2: string;
|
|
|
|
begin
|
|
{$ifdef macos}
|
|
if Lowercase(RedirStdIn) = 'stdin' then RedirStdIn := 'Dev:StdIn';
|
|
if Lowercase(RedirStdOut) = 'stdout' then RedirStdOut := 'Dev:Output';
|
|
if Lowercase(RedirStdOut) = 'stderr' then RedirStdOut := 'Dev:Error';
|
|
if Lowercase(RedirStdErr) = 'stdout' then RedirStdErr := 'Dev:Output';
|
|
if Lowercase(RedirStdErr) = 'stderr' then RedirStdErr := 'Dev:Error';
|
|
{$endif macos}
|
|
|
|
CmdLine2 := ComLine;
|
|
if RedirStdIn <> '' then CmdLine2 := CmdLine2 + ' < ' + RedirStdIn;
|
|
|
|
{$ifndef macos}
|
|
if RedirStdOut <> '' then CmdLine2 := CmdLine2 + ' > ' + RedirStdOut;
|
|
if RedirStdErr <> '' then
|
|
begin
|
|
if RedirStdErr = RedirStdOut then
|
|
CmdLine2 := CmdLine2 + ' 2>&1'
|
|
else
|
|
CmdLine2 := CmdLine2 + ' 2> ' + RedirStdErr;
|
|
end;
|
|
{$else macos}
|
|
if RedirStdErr <> RedirStdOut then
|
|
if RedirStdOut <> '' then CmdLine2 := CmdLine2 + ' > ' + RedirStdOut;
|
|
if RedirStdErr <> '' then
|
|
begin
|
|
if RedirStdErr = RedirStdOut then
|
|
CmdLine2 := CmdLine2 + ' ' + #183 + ' ' + RedirStdErr {#183 is "capital sigma" char in MacRoman}
|
|
else
|
|
CmdLine2 := CmdLine2 + ' ' + #179 + ' ' + RedirStdErr; {#179 is "greater or equal" char in MacRoman}
|
|
end;
|
|
{$endif macos}
|
|
|
|
DosExecute (ProgName, CmdLine2);
|
|
ExecuteRedir:=(IOStatus=0) and (ExecuteResult=0);
|
|
end;
|
|
|
|
{$ELSE SHELL_IMPLEMENTED}
|
|
function ExecuteRedir (Const ProgName, ComLine : String; RedirStdIn, RedirStdOut, RedirStdErr: String): boolean;
|
|
begin
|
|
ExecuteRedir:=false;
|
|
end;
|
|
{$ENDIF SHELL_IMPLEMENTED}
|
|
|
|
function ChangeRedirOut(Const Redir : String; AppendToFile : Boolean) : Boolean;
|
|
begin
|
|
ChangeRedirOut:=false;
|
|
end;
|
|
|
|
|
|
procedure RestoreRedirOut;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure DisableRedirOut;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure EnableRedirOut;
|
|
begin
|
|
end;
|
|
|
|
|
|
function ChangeRedirIn(Const Redir : String) : Boolean;
|
|
begin
|
|
ChangeRedirIn:=false;
|
|
end;
|
|
|
|
|
|
procedure RestoreRedirIn;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure DisableRedirIn;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure EnableRedirIn;
|
|
begin
|
|
end;
|
|
|
|
|
|
function ChangeRedirError(Const Redir : String; AppendToFile : Boolean) : Boolean;
|
|
begin
|
|
ChangeRedirError:=false;
|
|
end;
|
|
|
|
|
|
procedure RestoreRedirError;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure DisableRedirError;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure EnableRedirError;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure RedirDisableAll;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure RedirEnableAll;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure InitRedir;
|
|
begin
|
|
end;
|
|
{$endif not implemented}
|
|
|
|
{............................................................................}
|
|
|
|
procedure DosExecute(ProgName, ComLine : String);
|
|
{$ifdef windows}
|
|
var
|
|
StoreInherit : BOOL;
|
|
{$endif windows}
|
|
|
|
Begin
|
|
{$IfDef MsDos}
|
|
SmallHeap;
|
|
{$EndIf MsDos}
|
|
{$ifdef usedos}
|
|
SwapVectors;
|
|
{$endif usedos}
|
|
{ Must use shell() for linux for the wildcard expansion (PFV) }
|
|
{$ifdef UNIX}
|
|
IOStatus:=0;
|
|
ExecuteResult:=Shell(FixPath(Progname)+' '+Comline);
|
|
{$ifdef ver1_0}
|
|
{ Signal that causes the stop of the shell }
|
|
IOStatus:=ExecuteResult and $7F;
|
|
{ Exit Code seems to be in the second byte,
|
|
is this also true for BSD ??
|
|
$80 bit is a CoreFlag apparently }
|
|
ExecuteResult:=(ExecuteResult and $ff00) shr 8;
|
|
{$else}
|
|
if ExecuteResult<0 then
|
|
begin
|
|
IOStatus:=(-ExecuteResult) and $7f;
|
|
ExecuteResult:=((-ExecuteResult) and $ff00) shr 8;
|
|
end;
|
|
{$endif}
|
|
{$else}
|
|
{$ifdef windows}
|
|
StoreInherit:=ExecInheritsHandles;
|
|
ExecInheritsHandles:=true;
|
|
{ Avoid dialog boxes if dll loading fails }
|
|
SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
{$endif windows}
|
|
DosError:=0;
|
|
If UseComSpec then
|
|
Sysutils.ExecuteProcess (Getenv('COMSPEC'),'/C '+FixPath(progname)+' '+Comline)
|
|
else
|
|
begin
|
|
if LocateExeFile(progname) then
|
|
{$ifndef macos}
|
|
Sysutils.ExecuteProcess(ProgName,Comline)
|
|
{$else}
|
|
Dos.Exec(''''+ProgName+'''',Comline) {Quotes needed !}
|
|
{$endif}
|
|
else
|
|
DosError:=2;
|
|
end;
|
|
{$ifdef windows}
|
|
ExecInheritsHandles:=StoreInherit;
|
|
SetErrorMode(0);
|
|
{$endif windows}
|
|
IOStatus:=DosError;
|
|
ExecuteResult:=DosExitCode;
|
|
{$endif}
|
|
{$ifdef usedos}
|
|
SwapVectors;
|
|
{$endif}
|
|
{$ifdef CPU86}
|
|
{ reset the FPU }
|
|
{$asmmode att}
|
|
asm
|
|
fninit
|
|
end;
|
|
{$endif CPU86}
|
|
{$IfDef MsDos}
|
|
Fullheap;
|
|
{$EndIf MsDos}
|
|
End;
|
|
|
|
{*****************************************************************************
|
|
Initialize
|
|
*****************************************************************************}
|
|
|
|
initialization
|
|
New(FIn); New(FOut); New(FErr);
|
|
|
|
finalization
|
|
Dispose(FIn); Dispose(FOut); Dispose(FErr);
|
|
End.
|