lazarus/components/lazdebuggergdbmi/gdbmimiscclasses.pp
2022-03-24 00:39:03 +01:00

431 lines
11 KiB
ObjectPascal

{ ----------------------------------------------
GDBMIMiscClasses.pp - Debugger helper class
----------------------------------------------
This unit contains a helper class for decoding GDB output.
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code 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. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
* *
***************************************************************************
}
unit GDBMIMiscClasses;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
// DebuggerIntf
DbgIntfDebuggerBase,
// LazDebuggerGdbmi
DebugUtils, LazDebuggerIntf, LazDebuggerIntfBaseTypes;
type
TGDBMIResultFlags = set of (
rfNoMI, // flag is set if the output is not MI formatted
// some MI functions return normal output
// some normal functions return MI output
rfAsyncFailed //
);
TGDBMIExecResult = record
State: TDBGState;
Values: String;
Flags: TGDBMIResultFlags
end;
const
GDBMIExecResultDefault: TGDBMIExecResult = (State: dsNone; Values: ''; Flags: []);
type
PGDBMINameValue = ^TGDBMINameValue;
TGDBMINameValue = record
Name: TPCharWithLen;
Value: TPCharWithLen;
end;
{ TGDBMINameValueList }
TGDBMINameValueList = class(TObject)
private
FDataLen: Integer;
FText: String;
FData: PChar;
FCount: Integer;
FIndex: array of TGDBMINameValue;
FUseTrim: Boolean;
function Find(const AName : string): PGDBMINameValue;
function GetItem(const AIndex: Integer): PGDBMINameValue;
function GetText: String;
function GetValue(const AName : string): string;
function GetValuePtr(const AName: string): TPCharWithLen;
public
function GetString(const AIndex: Integer): string;
public
constructor Create(const AResultValues: String); overload;
constructor Create(const AResultValues: TPCharWithLen); overload;
constructor Create(AResult: TGDBMIExecResult); overload;
constructor Create(const AResultValues: String; const APath: array of String); overload;
constructor Create(AResult: TGDBMIExecResult; const APath: array of String); overload;
procedure Delete(AIndex: Integer);
procedure Init(const AResultValues: String);
procedure Init(AResultValues: PChar; ALength: Integer);
procedure Init(const AResultValues: TPCharWithLen);
procedure SetPath(const APath: String); overload;
procedure SetPath(const APath: array of String); overload;
function IndexOf(const AName: string): Integer;
property Count: Integer read FCount;
property Items[const AIndex: Integer]: PGDBMINameValue read GetItem;
property Values[const AName: string]: string read GetValue;
property ValuesPtr[const AName: string]: TPCharWithLen read GetValuePtr;
property UseTrim: Boolean read FUseTrim write FUseTrim;
property Data: PChar read FData;
property DataLen: Integer read FDataLen;
property Text: String read GetText;
end;
implementation
{ TGDBMINameValueList }
constructor TGDBMINameValueList.Create(const AResultValues: String);
begin
inherited Create;
Init(AResultValues);
end;
constructor TGDBMINameValueList.Create(const AResultValues: TPCharWithLen);
begin
inherited Create;
Init(AResultValues);
end;
constructor TGDBMINameValueList.Create(const AResultValues: String; const APath: array of String);
begin
inherited Create;
Init(AResultValues);
SetPath(APath);
end;
constructor TGDBMINameValueList.Create(AResult: TGDBMIExecResult);
begin
inherited Create;
Init(AResult.Values);
end;
constructor TGDBMINameValueList.Create(AResult: TGDBMIExecResult; const APath: array of String);
begin
inherited Create;
Init(AResult.Values);
SetPath(APath);
end;
procedure TGDBMINameValueList.Delete(AIndex: Integer);
begin
if AIndex < 0 then Exit;
if AIndex >= FCount then Exit;
Dec(FCount);
Move(FIndex[AIndex + 1], FIndex[AIndex], SizeOf(FIndex[0]) * (FCount - AIndex));
end;
function TGDBMINameValueList.Find(const AName: string): PGDBMINameValue;
var
n: Integer;
begin
n := IndexOf(AName);
if n < 0 then Exit(nil);
Result := @FIndex[n];
end;
function TGDBMINameValueList.GetItem(const AIndex: Integer): PGDBMINameValue;
begin
if AIndex < 0 then Exit(nil);
if AIndex >= FCount then Exit(nil);
Result := @FIndex[AIndex];
end;
function TGDBMINameValueList.GetText: String;
begin
Result := copy(FData, 1, FDataLen);
end;
function TGDBMINameValueList.GetString(const AIndex : Integer) : string;
var
len: Integer;
item: PGDBMINameValue;
begin
Result := '';
if (AIndex < 0) or (AIndex >= FCount) then Exit;
item := @FIndex[AIndex];
if item = nil then Exit;
len := Item^.Name.Len;
if Item^.Value.Ptr <> nil then begin
if (Item^.Value.Ptr-1) = '"' then inc(len, 2);
len := len + 1 + Item^.Value.Len;
end;
SetLength(Result, len);
if len > 0 then
Move(Item^.Name.Ptr^, Result[1], len);
end;
function TGDBMINameValueList.GetValue(const AName: string): string;
var
item: PGDBMINameValue;
begin
Result := '';
if FCount = 0 then Exit;
item := Find(AName);
if item = nil then Exit;
SetLength(Result, Item^.Value.Len);
if Item^.Value.Len > 0 then
Move(Item^.Value.Ptr^, Result[1], Item^.Value.Len);
end;
function TGDBMINameValueList.GetValuePtr(const AName: string): TPCharWithLen;
var
item: PGDBMINameValue;
begin
Result.Ptr := nil;
Result.Len := 0;
if FCount = 0 then Exit;
item := Find(AName);
if item = nil then Exit;
Result := item^.Value;
end;
procedure TGDBMINameValueList.Init(AResultValues: PChar; ALength: Integer);
function FindNextQuote(ACurPtr, AEndPtr: PChar): PChar;
begin
Result := ACurPtr;
while Result <= AEndPtr do
begin
case Result^ of
'\': Inc(Result, 2);
'"': Break;
else
Inc(Result);
end;
end;
end;
function FindClosingBracket(ACurPtr, AEndPtr: PChar): PChar;
var
deep: Integer;
begin
deep := 1;
Result := ACurPtr;
while Result <= AEndPtr do
begin
case Result^ of
'\': Inc(Result);
'"': Result := FindNextQuote(Result + 1, AEndPtr);
'[', '{': Inc(deep);
']', '}': begin
Dec(deep);
if deep = 0 then break;
end;
end;
Inc(Result);
end;
end;
procedure Add(AStartPtr, AEquPtr, AEndPtr: PChar);
var
Item: PGDBMINameValue;
begin
if AEndPtr <= AStartPtr then Exit;
// check space
if Length(FIndex) <= FCount
then SetLength(FIndex, FCount + 16);
Item := @FIndex[FCount];
if AEquPtr < AStartPtr
then begin
// trim spaces
if UseTrim then
begin
while (AStartPtr < AEndPtr) and (AStartPtr^ = #32) do
inc(AStartPtr);
while (AEndPtr > AStartPtr) and (AEndPtr^ = #32) do
dec(AEndPtr);
end;
// only name, no value
Item^.Name.Ptr := AStartPtr;
Item^.Name.Len := {%H-}PtrUInt(AEndPtr) - {%H-}PtrUInt(AStartPtr) + 1;
Item^.Value.Ptr := nil;
Item^.Value.Len := 0;
end
else begin
// trim surrounding spaces
if UseTrim then
begin
while (AStartPtr < AEquPtr) and (AStartPtr^ = #32) do
inc(AStartPtr);
while (AEndPtr > AEquPtr) and (AEndPtr^ = #32) do
dec(AEndPtr);
end;
Item^.Name.Ptr := AStartPtr;
Item^.Name.Len := {%H-}PtrUInt(AEquPtr) - {%H-}PtrUInt(AStartPtr);
// trim name spaces
if UseTrim then
while (Item^.Name.Len > 0) and (Item^.Name.Ptr[Item^.Name.Len - 1] = #32) do
dec(Item^.Name.Len);
if (AEquPtr < AEndPtr - 1) and (AEquPtr[1] = '"') and (AEndPtr^ = '"')
then begin
// strip surrounding "
Item^.Value.Ptr := AEquPtr + 2;
Item^.Value.Len := {%H-}PtrUInt(AEndPtr) - {%H-}PtrUInt(AEquPtr) - 2;
end
else begin
Item^.Value.Ptr := AEquPtr + 1;
Item^.Value.Len := {%H-}PtrUInt(AEndPtr) - {%H-}PtrUInt(AEquPtr)
end;
// trim value spaces
if UseTrim then
while (Item^.Value.Len > 0) and (Item^.Value.Ptr[0] = #32) do
begin
inc(Item^.Value.Ptr);
dec(Item^.Value.Len);
end;
end;
Inc(FCount);
end;
var
CurPtr, StartPtr, EquPtr, EndPtr: PChar;
begin
// clear
FCount := 0;
FData := AResultValues;
FDataLen := ALength;
if AResultValues = nil then Exit;
if ALength <= 0 then Exit;
EndPtr := AResultValues + ALength - 1;
// strip surrounding '[]' OR '{}' first
case AResultValues^ of
'[': begin
if EndPtr^ = ']'
then begin
Inc(AResultValues);
Dec(EndPtr);
end;
end;
'{': begin
if EndPtr^ = '}'
then begin
Inc(AResultValues);
Dec(EndPtr);
end;
end;
end;
StartPtr := AResultValues;
CurPtr := AResultValues;
EquPtr := nil;
while CurPtr <= EndPtr do
begin
case CurPtr^ of
'\': Inc(CurPtr); // skip escaped char
'"': CurPtr := FindNextQuote(CurPtr + 1, EndPtr);
'[',
'{': CurPtr := FindClosingBracket(CurPtr + 1, EndPtr);
'=': EquPtr := CurPtr;
',': begin
Add(StartPtr, EquPtr, CurPtr - 1);
Inc(CurPtr);
StartPtr := CurPtr;
Continue;
end;
end;
Inc(CurPtr);
end;
if StartPtr <= EndPtr
then Add(StartPtr, EquPtr, EndPtr);
end;
procedure TGDBMINameValueList.Init(const AResultValues: TPCharWithLen);
begin
Init(AResultValues.Ptr, AResultValues.Len)
end;
procedure TGDBMINameValueList.Init(const AResultValues: String);
begin
FText := AResultValues;
Init(PChar(FText), Length(FText));
end;
procedure TGDBMINameValueList.SetPath(const APath: String);
begin
SetPath([APath]);
end;
procedure TGDBMINameValueList.SetPath(const APath: array of String);
var
i: integer;
Item: PGDBMINameValue;
begin
for i := low(APath) to High(APath) do
begin
item := Find(APath[i]);
if item = nil
then begin
FCount := 0;
Exit;
end;
Init(Item^.Value);
end;
end;
function TGDBMINameValueList.IndexOf(const AName: string): Integer;
var
len: Integer;
begin
len := Length(AName);
Result := 0;
while Result < FCount do begin
if (FIndex[Result].Name.Len = len)
and (strlcomp(FIndex[Result].Name.Ptr, PChar(AName), len) = 0)
then exit;
inc(Result);
end;
Result := -1;
end;
end.