summaryrefslogtreecommitdiff
path: root/UCoreUtils.pas
diff options
context:
space:
mode:
authorTomas Bzatek <tbzatek@redhat.com>2025-11-28 21:36:41 +0100
committerTomas Bzatek <tbzatek@redhat.com>2025-11-28 21:36:41 +0100
commit69dd2b81de3bcbb955669f7937f3844b86467849 (patch)
tree1fdca6cce755961766f7436e2c83041710dca2b4 /UCoreUtils.pas
parent0ea64a41e1499d25296bdcc69fe207e20e545efd (diff)
downloadtuxcmd-69dd2b81de3bcbb955669f7937f3844b86467849.tar.xz
Port process spawning to g_spawn_*()
Diffstat (limited to 'UCoreUtils.pas')
-rw-r--r--UCoreUtils.pas426
1 files changed, 106 insertions, 320 deletions
diff --git a/UCoreUtils.pas b/UCoreUtils.pas
index 97777dc..eafd8bc 100644
--- a/UCoreUtils.pas
+++ b/UCoreUtils.pas
@@ -24,16 +24,12 @@ interface
uses SysUtils, Classes, lazglib2, lazgobject2, lazgdk3, lazgtk3, GTKClasses, UEngines, ULibc;
type
- PIntArray = ^TIntArray;
- TIntArray = array[0..1023] of LongWord;
-
PCharArray = array[0..0] of PChar;
TOpenStringArray = array of string;
TOpenPCharArray = array of PChar;
-const ConstERRSpawn = 26;
- ConstQuotationCharacters = [' ', '"', '''', '(', ')', ':', '&'];
+const ConstQuotationCharacters = [' ', '"', '''', '(', ')', ':', '&'];
ConstURIIllegalCharacters = '%:@/';
function FormatSize(Value: Int64; Base: integer; OverrideSizeFormat: integer = -1): string;
@@ -51,7 +47,6 @@ function OctalToAttr(Octal: Cardinal): Cardinal;
function PosEnd(Substr: string; S: string): Integer;
function GetHomePath: string;
function GetUserName: string;
-function GetHostName: string;
procedure SeparateExt(const Original: string; var Name, Ext: string);
procedure SeparateNameDir(Original: string; var Path, FileName: string);
function PadRightStr(const Str: string; Len: byte): string;
@@ -84,10 +79,9 @@ function QuoteMarkupStr(const Str: string; MarkupUnderscore: boolean = False): s
function RemoveQuotation(const Str: string): string;
function GetStrSize(s: string): Int64;
procedure DebugMsg(Params: array of const);
-function SpawnProcess(const AppPath: string; var Running: boolean; const Parameters: array of string): Cardinal;
-function ExecuteProgram(const AppCMDLine, CWD: string; const AutodetectGUI, RunInTerminal: boolean; var ErrorSignal: integer): boolean;
+function ExecuteProgram(const AppCMDLine, CWD: string; const AutodetectGUI, RunInTerminal: boolean; var ErrorString: string): boolean;
function IsItX11App(const Application: string): boolean;
-function HandleSystemCommand(const Command, ErrorText: string): boolean;
+function HandleSystemCommand(const Command: string; var ErrorString: string): boolean;
function CompareTextsEx(S1, S2: PChar): integer;
function LVCompareItems(Data1, Data2: PDataItem; const Ascending: boolean; const SortColumnID: integer): integer;
@@ -130,10 +124,7 @@ procedure InternalUnLock;
function InternalLockUnlocked: boolean;
procedure InternalLockInit(Locked: boolean);
-var ChildExitStatus: sig_atomic_t;
-
- AppPath, IconPath: string; // Used in UFileTypeSettings
-
+var AppPath, IconPath: string; // Used in UFileTypeSettings
NormalItemGDKColor, ActiveItemGDKColor, InactiveItemGDKColor, SelectedItemGDKColor, DotFileItemGDKColor, LinkItemGDKColor,
NormalItemGDKBackground, ActiveItemGDKBackground, InactiveItemGDKBackground: PGdkRGBA;
@@ -412,17 +403,6 @@ begin
end;
(********************************************************************************************************************************)
-function GetHostName: string;
-var s: PChar;
-begin
- s := malloc(65536);
- memset(s, 0, 65536);
- ULibc.gethostname(s, 65536);
- Result := String(strdup(s));
- libc_free(s);
-end;
-
-(********************************************************************************************************************************)
procedure SeparateExt(const Original: string; var Name, Ext: string);
var j: integer;
begin
@@ -1152,292 +1132,137 @@ begin
end;
(********************************************************************************************************************************)
-function SpawnProcess(const AppPath: string; var Running: boolean; const Parameters: array of string): Cardinal;
-var child_pid: __pid_t;
- args_list: PPChar;
+function ExecuteProgram(const AppCMDLine, CWD: string; const AutodetectGUI, RunInTerminal: boolean; var ErrorString: string): boolean;
+var s: string;
+ Term: boolean;
i: integer;
- sv: sigval_t;
-begin
- Result := 0;
- Running := False;
- ChildExitStatus := -1;
-
- // Make the args_list array
- args_list := nil;
- if Length(Parameters) > 0 then begin
- args_list := malloc((Length(Parameters) + 1) * sizeof(PChar));
- memset(args_list, 0, (Length(Parameters) + 1) * sizeof(PChar));
- for I := 0 to Length(Parameters) - 1 do
- begin
- PCharArray(args_list^)[I] := strdup(PChar(Parameters[i]));
- end;
- PCharArray(args_list^)[Length(Parameters)] := nil;
- end;
-
- // Duplicate this process
- DebugMsg(['SpawnProcess: before fork']);
- child_pid := fork;
- if child_pid <> 0 then begin
- Result := child_pid;
- Sleep(100);
- //* FIXME: strange behaviour when freed
-{ for i := 0 to Length(Parameters) - 1 do
- if PCharArray(args_list^)[i] <> nil then
- libc_free(PCharArray(args_list^)[i]); }
- if args_list <> nil then libc_free(args_list);
- Application.ProcessMessages;
- Running := ChildExitStatus < 0;
- DebugMsg(['SpawnProcess: ChildExitStatus = ', ChildExitStatus]);
- if not Running then Result := 0;
- if not WIFEXITED(ChildExitStatus) then Result := WTERMSIG(ChildExitStatus);
- DebugMsg(['SpawnProcess: Result = ', Result]);
- end else begin
- // Now execute AppPath, searching for it in the path
- execvp(PChar(AppPath), args_list);
- // The execvp function returns only if an error occurs
- sigqueue(getppid, SIGUSR1, sv);
- DebugMsg(['SpawnProcess: forked: error, sending SIGUSR1']);
- _exit(ConstERRSpawn);
- end;
- ChildExitStatus := -1;
-end;
-
-(********************************************************************************************************************************)
-procedure SplitArgs(var Args: TOpenStringArray; CMDLine: string);
-var InQuotes: boolean;
- i, Start: integer;
- QuoteChar: char;
- s: string;
-begin
- SetLength(Args, 0);
- InQuotes := False;
- CMDLine := Trim(CMDLine);
- if Length(CMDLine) = 0 then Exit;
- Start := 1;
- QuoteChar := #0;
- for i := 1 to Length(CMDLine) do
- case CMDLine[i] of
- ' ': if (not InQuotes) and ((i = 1) or (CMDLine[i - 1] <> '\')) then begin
- s := Trim(Copy(CMDLine, Start, i - Start));
- TrimQuotes(s);
- Start := i;
- if s = '' then Continue;
- SetLength(Args, Length(Args) + 1);
- Args[Length(Args) - 1] := s;
- end;
- '"', '''': if (i = 1) or (CMDLine[i - 1] <> '\') then
- if not InQuotes then begin
- InQuotes := True;
- QuoteChar := CMDLine[i];
-// Start := i;
- end else
- if CMDLine[i] = QuoteChar then begin
- InQuotes := False;
- s := Trim(Copy(CMDLine, Start, i + 1 - Start));
- TrimQuotes(s);
- Start := i;
- if s = '' then Continue;
- if (Pos('"', s) > 1) and (Pos('"', s) < Length(s)) and (NumCountChars('"', s) mod 2 = 1) then s := s + '"';
-// if (Pos('''', s) > 1) and (Pos('''', s) < Length(s)) and (NumCountChars('''', s) mod 2 = 1) then s := s + '''';
- SetLength(Args, Length(Args) + 1);
- Args[Length(Args) - 1] := s;
- end;
- end;
- if (Start <> Length(CMDLine)) or (Start = 1) then begin
- SetLength(Args, Length(Args) + 1);
- Args[Length(Args) - 1] := Trim(Copy(CMDLine, Start, Length(CMDLine) + 1 - Start));
- TrimQuotes(Args[Length(Args) - 1]);
- end;
-end;
-
-function ExecuteProgram(const AppCMDLine, CWD: string; const AutodetectGUI, RunInTerminal: boolean; var ErrorSignal: integer): boolean;
-var Args: TOpenStringArray;
- s, s2: string;
- Running, Term: boolean;
- x: integer;
+ argvp: PPgchar;
+ p: Pgchar;
+ pcwd: Pgchar;
+ error: PGerror;
begin
+ error := nil;
Result := False;
- try
- DebugMsg(['*** Running ExecuteProgram begin']);
-// DebugMsg(['ExecuteProgram: ConfTerminalCommand = "', ConfTerminalCommand, '"']);
- s := Trim(AppCMDLine);
- ErrorSignal := 0;
- Term := RunInTerminal;
- SplitArgs(Args, s);
-
- if AutodetectGUI then
- if Length(Trim(Args[0])) > 0 then Term := not IsItX11App(Trim(Args[0]));
-
- if Term then begin
- x := 1;
- while x <= Length(s) do begin
- if (s[x] in [{'"',} '''']) and ((x = 1) or (s[x - 1] <> '\')) then
- Insert('\', s, x);
- Inc(x);
- end;
- s2 := ReplaceStr(ConfTerminalCommand, '%cwd', QuoteStr(CWD));
- s := Format(s2, [s]);
- SplitArgs(Args, s);
+ DebugMsg(['*** Running ExecuteProgram begin: ', AppCMDLine]);
+ s := Trim(AppCMDLine);
+ Term := RunInTerminal;
+
+ if AutodetectGUI then Term := not IsItX11App(s);
+
+ if Term then begin
+ if not g_shell_parse_argv(PChar(ConfTerminalCommand), nil, @argvp, @error) then begin
+ ErrorString := String(error^.message);
+ DebugMsg(['ExecuteProgram: Error parsing commandline: ', ErrorString]);
+ g_error_free(error);
+ Exit;
end;
- if ConfUseLibcSystem then begin
- s := s + ' &';
- DebugMsg([s]);
- DebugMsg(['**** Running system']);
- x := libc_system(PChar(s));
- Result := x <> -1; // -1 means fork failed
- DebugMsg(['**** Running system = ', x, ' --- done']);
- end else begin
- if Length(Args) = 0 then Exit;
- for x := 0 to Length(Args) - 1 do Args[x] := RemoveQuotation(Args[x]);
- DebugMsg(['**** Running spawn']);
- x := -1;
- try
- x := SpawnProcess(Args[0], Running, Args);
- except
- on E: Exception do DebugMsg(['ExecuteProgram(AppCMDLine = ''', AppCMDLine, '''): Exception: ', E.Message]);
+ for i := 0 to g_strv_length(argvp) - 1 do begin
+ if strstr(argvp[i], '%s') <> nil then begin
+ p := argvp[i];
+ argvp[i] := g_strdup_printf(p, [PChar(AppCMDLine)]);
+ g_free(p);
end;
- DebugMsg(['**** Running spawn -- done']);
- Result := Running;
- if not Running then ErrorSignal := x;
+ if strstr(argvp[i], '%cwd') <> nil then begin
+ // TODO: g_shell_quote() the argument?
+ s := ReplaceStr(String(argvp[i]), '%cwd', QuoteStr(CWD));
+ g_free(argvp[i]);
+ argvp[i] := g_strdup(PChar(s));
+ end;
+ end;
+ end else begin
+ if not g_shell_parse_argv(PChar(s), nil, @argvp, @error) then begin
+ ErrorString := String(error^.message);
+ DebugMsg(['ExecuteProgram: Error parsing commandline: ', ErrorString]);
+ g_error_free(error);
+ Exit;
end;
+ end;
- except
+ if ParamDebug then
+ for i := 0 to g_strv_length(argvp) - 1 do
+ DebugMsg([' argvp[', i, '] = "', argvp[i], '"']);
+
+ pcwd := nil;
+ if Length(CWD) > 0 then pcwd := PChar(CWD);
+ Result := g_spawn_async(pcwd, argvp, nil,
+ [G_SPAWN_SEARCH_PATH, G_SPAWN_STDOUT_TO_DEV_NULL, G_SPAWN_STDERR_TO_DEV_NULL, G_SPAWN_CLOEXEC_PIPES],
+ nil, nil, nil, @error);
+ if not Result then begin
+ ErrorString := String(error^.message);
+ DebugMsg(['ExecuteProgram: Error spawning command: ', ErrorString]);
+ g_error_free(error);
end;
DebugMsg(['*** Running ExecuteProgram end']);
end;
(********************************************************************************************************************************)
function IsItX11App(const Application: string): boolean;
-const BSize = 65536;
- What = 'libX11.so';
-var stream: PFILE;
- Buffer: Pointer;
- i: integer;
- str: string;
+var environ: PPgchar;
+ argvp: PPgchar;
+ stdout: Pgchar;
+ error: PGerror;
begin
Result := False;
DebugMsg(['***** function IsItX11App(''', Application, ''') begin --']);
- try
-// setenv('LD_TRACE_LOADED_OBJECTS', '1', True);
- stream := popen(PChar('LD_TRACE_LOADED_OBJECTS=1 ' + Application), 'r');
-// stream := popen(PChar(Application), 'r');
- DebugMsg(['***** IsItX11App: popen OK']);
- if Assigned(stream) then begin
- Buffer := malloc(BSize);
- if buffer = nil then Writeln('buffer nil: ', integer(errno));
- if stream = nil then Writeln('stream nil');
- memset(Buffer, 0, BSize);
- DebugMsg(['***** IsItX11App: malloc() OK']);
-
- while feof(stream) = 0 do begin
- i := fread(Buffer, 1, BSize, stream);
- if i > 0 then begin
- SetLength(str, i);
- memcpy(@str[1], Buffer, i);
- Result := Result or (Pos(What, str) > 0);
- end;
- end;
- pclose(stream);
- libc_free(Buffer);
- SetLength(str, 0);
- end;
-// unsetenv('LD_TRACE_LOADED_OBJECTS');
-
- except
- on E: Exception do DebugMsg(['*** IsItX11App(''', Application, '''):Exception: ', E.Message]);
+ error := nil;
+ stdout := nil;
+ if not g_shell_parse_argv(PChar(Application), nil, @argvp, @error) then begin
+ DebugMsg(['IsItX11App: Error parsing commandline: ', String(error^.message)]);
+ g_error_free(error);
+ Exit;
end;
+
+ environ := g_get_environ();
+ environ := g_environ_setenv(environ, 'LD_TRACE_LOADED_OBJECTS', '1', True);
+ if not g_spawn_sync(nil, argvp, environ,
+ [G_SPAWN_SEARCH_PATH, G_SPAWN_STDERR_TO_DEV_NULL, G_SPAWN_CLOEXEC_PIPES],
+ nil, nil, @stdout, nil, nil, @error)
+ then begin
+ DebugMsg(['IsItX11App: Error spawning command: ', String(error^.message)]);
+ g_error_free(error);
+ end else Result := strstr(stdout, 'libX11.so') <> nil;
+
+ g_free(stdout);
+ g_strfreev(environ);
+ g_strfreev(argvp);
+
DebugMsg(['***** function IsItX11App(''', Application, ''') = ', Result]);
end;
-function HandleSystemCommand(const Command, ErrorText: string): boolean;
-const BSize = 65536;
-var stream: PFILE;
- Buffer: Pointer;
- i, NumRead: integer;
- child_pid: __pid_t;
- fds: array[0..1] of integer;
- Parameters: TOpenStringArray;
- args_list: System.PPChar;
- Temp, s: string;
+function HandleSystemCommand(const Command: string; var ErrorString: string): boolean;
+var argvp: PPgchar;
+ stderr: Pgchar;
+ wait_status: gint;
+ error: PGerror;
begin
Result := False;
DebugMsg(['***** function HandleSystemCommand(''', Command, ''') begin --']);
- try
- DebugMsg(['***** HandleSystemCommand: before fork']);
- pipe(@fds);
- child_pid := fork;
- DebugMsg(['***** HandleSystemCommand: fork, child_pid = ', child_pid]);
-
-
- // Main application
- if child_pid <> 0 then begin
- libc_close(fds[1]);
- stream := fdopen(fds[0], 'r');
- Buffer := malloc(BSize);
-// DebugMsg(['x0']);
- memset(Buffer, 0, BSize);
-// DebugMsg(['x1']);
- if buffer = nil then Writeln('buffer nil: ', integer(errno));
- if stream = nil then Writeln('stream nil');
-
- SetLength(s, 0);
- while feof(stream) = 0 do begin
- NumRead := fread(Buffer, 1, BSize, stream);
- DebugMsg(['***** HandleSystemCommand: NumRead = ', NumRead]);
- if NumRead > 0 then begin
- SetLength(s, Length(s) + NumRead);
- memcpy(@s[Length(s) - NumRead + 1], Buffer, NumRead);
- end;
- end;
- libc_close(fds[0]);
-// DebugMsg(['x2']);
- TrimCRLFESC(s);
-// DebugMsg(['x3']);
-
- libc_free(Buffer);
-// DebugMsg(['x4']);
- end
+ error := nil;
+ stderr := nil;
+ if not g_shell_parse_argv(PChar(Command), nil, @argvp, @error) then begin
+ ErrorString := String(error^.message);
+ DebugMsg(['HandleSystemCommand: Error parsing commandline: ', ErrorString]);
+ g_error_free(error);
+ Exit;
+ end;
- // forked PID
- else begin
- args_list := nil;
- try
- SplitArgs(Parameters, Command);
- // Fill the args_list array
- if Length(Parameters) > 0 then begin
- args_list := malloc((Length(Parameters) + 1) * SizeOf(PChar));
- memset(args_list, 0, (Length(Parameters) + 1) * SizeOf(PChar));
- for I := 0 to Length(Parameters) - 1 do
- begin
- Temp := Parameters[i];
-// PCharArray(args_list^)[I] := malloc(Length(Temp)+1);
-// memset(PCharArray(args_list^)[I], 0, Length(Temp)+1);
-// StrCopy(PCharArray(args_list^)[I], PChar(Temp));
- PCharArray(args_list^)[I] := strdup(PChar(Temp));
- end;
- PCharArray(args_list^)[Length(Parameters)] := nil;
- end;
- except
- on E: Exception do DebugMsg(['*** forked ** function HandleSystemCommand(''', Command, '''):Exception: ', E.Message]);
- end;
+ Result := g_spawn_sync(nil, argvp, nil, [G_SPAWN_SEARCH_PATH, G_SPAWN_CLOEXEC_PIPES],
+ nil, nil, nil, @stderr, @wait_status, @error);
+ if Result then Result := g_spawn_check_exit_status(wait_status, @error);
+ if not Result then begin
+ if (stderr <> nil) and (strlen(stderr) > 0)
+ then ErrorString := String(stderr)
+ else ErrorString := String(error^.message);
+ DebugMsg(['HandleSystemCommand:: Error spawning command: ', ErrorString]);
+ g_error_free(error);
+ end;
- libc_close(fds[0]); // Close copy of reader file descriptor
- dup2(fds[1], STDERR_FILENO);
- execvp(PChar(Parameters[0]), args_list);
- DebugMsg(['***** HandleSystemCommand: failed execvp: something went wrong...']);
- WriteLn(erroutput, 'Error executing command');
- _exit(ConstERRSpawn);
- end;
+ g_free(stderr);
+ g_strfreev(argvp);
- Result := Length(s) = 0;
- if not Result then Application.MessageBox(Format('%s%s', [ErrorText, StrToUTF8(s)]), [mbOK], mbError, mbOK, mbOK);
- SetLength(s, 0);
- except
- on E: Exception do DebugMsg(['***** function HandleSystemCommand(''', Command, '''):Exception: ', E.Message]);
- end;
DebugMsg(['***** finished function HandleSystemCommand(''', Command, ''') = ', Result]);
end;
@@ -1672,44 +1497,6 @@ begin
end;
(********************************************************************************************************************************)
-
-procedure signal_proc(signal_number: integer); cdecl;
-var {pid,} status: integer;
-begin
-// !!!!!!!!!! Warning
-// There should be no debug outputs in this function because it probably cause program freezes after fork
-// I mean REALLY NO outputs to console
-
-// DebugMsg(['SIGCHLD signal received']);
-// DebugMsg(['Signal received: ', signal_number]);
-// DebugMsg(['*** Signal received: ', signal_number, ' = ', GetSignalString(signal_number)]);
- case signal_number of
-{ SIGUSR1: begin
- DebugMsg(['begin wait']);
- wait(@status);
- DebugMsg(['end wait']);
- ChildExitStatus := status;
- end;}
- SIGCHLD, SIGUSR1: begin
-// DebugMsg(['begin wait']);
-// wait(@status);
- {pid :=} waitpid(-1, @status, WNOHANG);
-// DebugMsg(['**** waitpid result: pid = ', pid, ', status = ', status]);
-// DebugMsg(['end wait']);
- if signal_number = SIGUSR1 then ChildExitStatus := status;
- end;
- end;
-end;
-
-procedure SetupSignals;
-var sigchld_action: _sigaction;
-begin
- memset(@sigchld_action, 0, SizeOf(__sigaction));
- sigchld_action.__sigaction_handler := @signal_proc;
- sigaction(SIGUSR1, @sigchld_action, nil);
- sigaction(SIGCHLD, @sigchld_action, nil);
-end;
-
procedure SetupColors;
begin
if Assigned(NormalItemGDKColor) then gdk_rgba_free(NormalItemGDKColor);
@@ -1844,7 +1631,6 @@ initialization
// Kylix behaves fine, only forward slash is honored.
AllowDirectorySeparators := ['/'];
InternalLockInit(True);
- SetupSignals;
NormalItemGDKColor := nil;
ActiveItemGDKColor := nil;