diff options
Diffstat (limited to 'UCoreUtils.pas')
| -rw-r--r-- | UCoreUtils.pas | 426 |
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; |
