cosmemilton-acbr-node 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +502 -502
- package/NOTICE +8 -8
- package/README.md +40 -11
- package/acbr.config.json +16 -16
- package/acbr.lock.json +0 -0
- package/dist/cli.js +39 -13
- package/dist/cli.js.map +2 -2
- package/dist/contract.json +0 -0
- package/dist/index.cjs +36 -11
- package/dist/index.cjs.map +2 -2
- package/dist/index.js +36 -11
- package/dist/index.js.map +2 -2
- package/dist/runtime.cjs +36 -11
- package/dist/runtime.cjs.map +2 -2
- package/dist/runtime.js +36 -11
- package/dist/runtime.js.map +2 -2
- package/dist/types/generated/acbr/index.d.ts +1 -0
- package/dist/types/generated/acbr/models.d.ts +0 -0
- package/dist/types/src/cli.d.ts +0 -0
- package/dist/types/src/config.d.ts +0 -0
- package/dist/types/src/contract-help.d.ts +0 -0
- package/dist/types/src/diagnostics.d.ts +2 -0
- package/dist/types/src/generator.d.ts +0 -0
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/native-build.d.ts +1 -0
- package/dist/types/src/offline-check.d.ts +0 -0
- package/dist/types/src/offline-fixture.d.ts +0 -0
- package/dist/types/src/runtime.d.ts +1 -0
- package/dist/types/src/source.d.ts +0 -0
- package/dist/types/src/types.d.ts +4 -2
- package/docs/API.md +116 -90
- package/docs/ARQUITETURA.md +19 -15
- package/docs/CI.md +60 -60
- package/docs/CLI.md +1 -1
- package/docs/DEPENDENCIAS-NATIVAS.md +93 -93
- package/docs/VALIDACAO.md +67 -67
- package/docs/examples/recuperar-xml.mjs +54 -0
- package/docs/licenses/fpc/COPYING.FPC +25 -25
- package/docs/licenses/fpc/COPYING.GPL.txt +340 -340
- package/docs/licenses/fpc/COPYING.LGPL.txt +504 -504
- package/docs/licenses/fpc/ORIGIN.json +21 -21
- package/docs/licenses/lazarus/COPYING.LGPL.txt +481 -481
- package/docs/licenses/lazarus/COPYING.modifiedLGPL.txt +26 -26
- package/docs/licenses/lazarus/COPYING.txt +46 -46
- package/docs/licenses/lazarus/ORIGIN.json +1 -1
- package/native/NativeMotor.pas +55 -3
- package/native/NativeProtocol.pas +3 -2
- package/native/NativeRecovery.pas +274 -0
- package/native/generated/AcbrModels.pas +0 -0
- package/package.json +76 -76
- package/scripts/build-native-deps.mjs +237 -237
- package/tools/extract-model.lpr +119 -119
package/tools/extract-model.lpr
CHANGED
|
@@ -1,119 +1,119 @@
|
|
|
1
|
-
program ExtractModel;
|
|
2
|
-
{$mode objfpc}{$H+}
|
|
3
|
-
uses Classes, SysUtils, Contnrs, fpjson, PScanner, PParser, PasTree;
|
|
4
|
-
type
|
|
5
|
-
TContainer = class(TPasTreeContainer)
|
|
6
|
-
function CreateElement(C: TPTreeElement; const N: string; P: TPasElement;
|
|
7
|
-
V: TPasMemberVisibility; const F: string; L: Integer): TPasElement; override;
|
|
8
|
-
function FindElement(const N: string): TPasElement; override;
|
|
9
|
-
end;
|
|
10
|
-
function TContainer.CreateElement(C: TPTreeElement; const N: string; P: TPasElement;
|
|
11
|
-
V: TPasMemberVisibility; const F: string; L: Integer): TPasElement;
|
|
12
|
-
begin Result:=C.Create(N,P); Result.Visibility:=V; Result.SourceFilename:=F; Result.SourceLinenumber:=L end;
|
|
13
|
-
function TContainer.FindElement(const N: string): TPasElement;
|
|
14
|
-
begin Result:=nil end;
|
|
15
|
-
var Root,IncDir:string; Files,Queue,Seen:TStringList; Modules:TObjectList; Units:TJSONArray;
|
|
16
|
-
procedure IndexDir(const D:string);
|
|
17
|
-
var S:TSearchRec; F:string;
|
|
18
|
-
begin
|
|
19
|
-
if FindFirst(IncludeTrailingPathDelimiter(D)+'*',faAnyFile,S)=0 then begin
|
|
20
|
-
repeat
|
|
21
|
-
if (S.Name='.') or (S.Name='..') then continue;
|
|
22
|
-
F:=IncludeTrailingPathDelimiter(D)+S.Name;
|
|
23
|
-
if (S.Attr and faDirectory)<>0 then IndexDir(F)
|
|
24
|
-
else if SameText(ExtractFileExt(F),'.pas') or SameText(ExtractFileExt(F),'.pp') then
|
|
25
|
-
Files.Values[LowerCase(ChangeFileExt(S.Name,''))]:=F;
|
|
26
|
-
until FindNext(S)<>0; FindClose(S);
|
|
27
|
-
end;
|
|
28
|
-
end;
|
|
29
|
-
function TypeName(T:TPasType):string;
|
|
30
|
-
begin if T=nil then Result:='' else Result:=T.Name end;
|
|
31
|
-
function Expr(E:TPasExpr):string;
|
|
32
|
-
begin if E=nil then Result:='' else Result:=E.GetDeclaration(true) end;
|
|
33
|
-
function PublicMember(E:TPasElement):boolean;
|
|
34
|
-
begin Result:=E.Visibility in [visDefault,visPublic,visPublished] end;
|
|
35
|
-
function Node(E:TPasElement):TJSONObject;
|
|
36
|
-
var A,B:TJSONArray; C:TPasClassType; P:TPasProperty; I:integer; M:TPasElement; O:TJSONObject;
|
|
37
|
-
begin
|
|
38
|
-
Result:=TJSONObject.Create(['name',E.Name,'line',E.SourceLinenumber]);
|
|
39
|
-
if E is TPasClassType then begin
|
|
40
|
-
C:=TPasClassType(E); Result.Add('kind','class'); Result.Add('ancestor',TypeName(C.AncestorType));
|
|
41
|
-
A:=TJSONArray.Create; B:=TJSONArray.Create; Result.Add('properties',A);Result.Add('methods',B);
|
|
42
|
-
for I:=0 to C.Members.Count-1 do begin
|
|
43
|
-
M:=TPasElement(C.Members[I]); if not PublicMember(M) then continue;
|
|
44
|
-
if M is TPasProperty then begin
|
|
45
|
-
P:=TPasProperty(M);
|
|
46
|
-
O:=TJSONObject.Create(['name',P.Name,'type',TypeName(P.VarType),'indexed',P.Args.Count>0,
|
|
47
|
-
'readable',Assigned(P.ReadAccessor) or (P.ReadAccessorName<>''),'writable',Assigned(P.WriteAccessor) or (P.WriteAccessorName<>''),
|
|
48
|
-
'default',P.DefaultValue,'line',P.SourceLinenumber]); A.Add(O);
|
|
49
|
-
end else if M is TPasProcedure then begin
|
|
50
|
-
O:=TJSONObject.Create(['name',M.Name,'result','','arguments',TPasProcedure(M).ProcType.Args.Count]);
|
|
51
|
-
if TPasProcedure(M).ProcType is TPasFunctionType then
|
|
52
|
-
O.Strings['result']:=TypeName(TPasFunctionType(TPasProcedure(M).ProcType).ResultEl.ResultType);
|
|
53
|
-
B.Add(O);
|
|
54
|
-
end;
|
|
55
|
-
end;
|
|
56
|
-
end else if E is TPasEnumType then begin
|
|
57
|
-
Result.Add('kind','enum'); A:=TJSONArray.Create;Result.Add('values',A);
|
|
58
|
-
for I:=0 to TPasEnumType(E).Values.Count-1 do begin
|
|
59
|
-
M:=TPasElement(TPasEnumType(E).Values[I]);
|
|
60
|
-
A.Add(TJSONObject.Create(['name',M.Name,'assigned',TPasEnumValue(M).AssignedValue]));
|
|
61
|
-
end;
|
|
62
|
-
end else if E is TPasAliasType then begin
|
|
63
|
-
Result.Add('kind','alias');Result.Add('target',TypeName(TPasAliasType(E).DestType));
|
|
64
|
-
end else if E is TPasRangeType then begin
|
|
65
|
-
Result.Add('kind','range');Result.Add('min',TPasRangeType(E).RangeStart);Result.Add('max',TPasRangeType(E).RangeEnd);
|
|
66
|
-
end else if E is TPasType then begin
|
|
67
|
-
Result.Add('kind','unsupported');Result.Add('declarationKind',E.ClassName);
|
|
68
|
-
end else begin Result.Free; Result:=nil end;
|
|
69
|
-
end;
|
|
70
|
-
procedure ParseUnitFile(const N:string);
|
|
71
|
-
var F:string; R:TFileResolver; S:TPascalScanner; P:TPasParser; C:TContainer;
|
|
72
|
-
M:TPasModule; U:TJSONObject; A,B:TJSONArray; I:integer; O:TJSONObject; E:TPasElement;
|
|
73
|
-
begin
|
|
74
|
-
if Seen.IndexOf(LowerCase(N))>=0 then exit;Seen.Add(LowerCase(N));
|
|
75
|
-
F:=Files.Values[LowerCase(N)];if F='' then exit;
|
|
76
|
-
R:=TFileResolver.Create; S:=nil;P:=nil;C:=nil;M:=nil;
|
|
77
|
-
try
|
|
78
|
-
R.BaseDirectory:=ExtractFilePath(F);R.AddIncludePath(IncDir);R.AddIncludePath(ExtractFilePath(F));
|
|
79
|
-
S:=TPascalScanner.Create(R); S.AddDefine('FPC');S.AddDefine('FPK');
|
|
80
|
-
if SameText(ParamStr(3),'win64') then begin S.AddDefine('MSWINDOWS');S.AddDefine('WINDOWS');S.AddDefine('WIN64') end
|
|
81
|
-
else begin S.AddDefine('LINUX');S.AddDefine('UNIX') end;
|
|
82
|
-
S.AddDefine('CPUX86_64');S.AddDefine('CPU64');S.AddMacro('FPC_FULLVERSION','30202');S.AddDefine('VER3_2');S.AddDefine('NOGUI');S.AddDefine('NOREPORT');
|
|
83
|
-
S.OpenFile(F);C:=TContainer.Create;P:=TPasParser.Create(S,R,C);
|
|
84
|
-
P.Options:=P.Options+[po_IgnoreUnknownResource,po_NoOverloadedProcs,po_AsmWhole];
|
|
85
|
-
P.ParseMain(M);Modules.Add(M);
|
|
86
|
-
U:=TJSONObject.Create(['name',M.Name,'file',Copy(F,Length(Root)+2,MaxInt)]);
|
|
87
|
-
A:=TJSONArray.Create;B:=TJSONArray.Create;U.Add('uses',A);U.Add('types',B);
|
|
88
|
-
for I:=0 to High(M.InterfaceSection.UsesClause) do begin
|
|
89
|
-
E:=M.InterfaceSection.UsesClause[I]; A.Add(E.Name);
|
|
90
|
-
if not SameText(E.Name,'ACBrBase') and not (Pos('ACBrUtil.',E.Name)=1) then Queue.Add(E.Name);
|
|
91
|
-
end;
|
|
92
|
-
for I:=0 to M.InterfaceSection.Types.Count-1 do begin
|
|
93
|
-
O:=Node(TPasElement(M.InterfaceSection.Types[I]));if O<>nil then B.Add(O);
|
|
94
|
-
end;
|
|
95
|
-
for I:=0 to M.InterfaceSection.Classes.Count-1 do begin
|
|
96
|
-
O:=Node(TPasElement(M.InterfaceSection.Classes[I]));if O<>nil then B.Add(O);
|
|
97
|
-
end;
|
|
98
|
-
Units.Add(U);
|
|
99
|
-
except on Ex:Exception do begin
|
|
100
|
-
WriteLn(StdErr,'Cannot parse ',F,': ',Ex.Message);Halt(2);
|
|
101
|
-
end end;
|
|
102
|
-
P.Free;S.Free;C.Free;R.Free;
|
|
103
|
-
end;
|
|
104
|
-
var I:integer; Output:TJSONObject; OutText:TStringList;
|
|
105
|
-
begin
|
|
106
|
-
if ParamCount<2 then begin WriteLn(StdErr,'Usage: extract-model SOURCE_ROOT OUTPUT_JSON');Halt(1) end;
|
|
107
|
-
Root:=ExcludeTrailingPathDelimiter(ExpandFileName(ParamStr(1)));
|
|
108
|
-
IncDir:=Root+'/Fontes/ACBrComum';
|
|
109
|
-
Files:=TStringList.Create;Files.CaseSensitive:=false;Queue:=TStringList.Create;
|
|
110
|
-
Seen:=TStringList.Create;Modules:=TObjectList.Create(true);Units:=TJSONArray.Create;
|
|
111
|
-
try
|
|
112
|
-
IndexDir(Root+'/Fontes');Queue.Add('ACBrNFe.Classes');I:=0;
|
|
113
|
-
while I<Queue.Count do begin ParseUnitFile(Queue[I]);Inc(I) end;
|
|
114
|
-
if Units.Count=0 then raise Exception.Create('ACBrNFe.Classes source was not found');
|
|
115
|
-
Output:=TJSONObject.Create(['schemaVersion',1,'root','TNFe']);Output.Add('units',Units);
|
|
116
|
-
OutText:=TStringList.Create;OutText.Text:=Output.FormatJSON;OutText.SaveToFile(ParamStr(2));
|
|
117
|
-
OutText.Free;Output.Free;
|
|
118
|
-
finally Modules.Free;Seen.Free;Queue.Free;Files.Free end;
|
|
119
|
-
end.
|
|
1
|
+
program ExtractModel;
|
|
2
|
+
{$mode objfpc}{$H+}
|
|
3
|
+
uses Classes, SysUtils, Contnrs, fpjson, PScanner, PParser, PasTree;
|
|
4
|
+
type
|
|
5
|
+
TContainer = class(TPasTreeContainer)
|
|
6
|
+
function CreateElement(C: TPTreeElement; const N: string; P: TPasElement;
|
|
7
|
+
V: TPasMemberVisibility; const F: string; L: Integer): TPasElement; override;
|
|
8
|
+
function FindElement(const N: string): TPasElement; override;
|
|
9
|
+
end;
|
|
10
|
+
function TContainer.CreateElement(C: TPTreeElement; const N: string; P: TPasElement;
|
|
11
|
+
V: TPasMemberVisibility; const F: string; L: Integer): TPasElement;
|
|
12
|
+
begin Result:=C.Create(N,P); Result.Visibility:=V; Result.SourceFilename:=F; Result.SourceLinenumber:=L end;
|
|
13
|
+
function TContainer.FindElement(const N: string): TPasElement;
|
|
14
|
+
begin Result:=nil end;
|
|
15
|
+
var Root,IncDir:string; Files,Queue,Seen:TStringList; Modules:TObjectList; Units:TJSONArray;
|
|
16
|
+
procedure IndexDir(const D:string);
|
|
17
|
+
var S:TSearchRec; F:string;
|
|
18
|
+
begin
|
|
19
|
+
if FindFirst(IncludeTrailingPathDelimiter(D)+'*',faAnyFile,S)=0 then begin
|
|
20
|
+
repeat
|
|
21
|
+
if (S.Name='.') or (S.Name='..') then continue;
|
|
22
|
+
F:=IncludeTrailingPathDelimiter(D)+S.Name;
|
|
23
|
+
if (S.Attr and faDirectory)<>0 then IndexDir(F)
|
|
24
|
+
else if SameText(ExtractFileExt(F),'.pas') or SameText(ExtractFileExt(F),'.pp') then
|
|
25
|
+
Files.Values[LowerCase(ChangeFileExt(S.Name,''))]:=F;
|
|
26
|
+
until FindNext(S)<>0; FindClose(S);
|
|
27
|
+
end;
|
|
28
|
+
end;
|
|
29
|
+
function TypeName(T:TPasType):string;
|
|
30
|
+
begin if T=nil then Result:='' else Result:=T.Name end;
|
|
31
|
+
function Expr(E:TPasExpr):string;
|
|
32
|
+
begin if E=nil then Result:='' else Result:=E.GetDeclaration(true) end;
|
|
33
|
+
function PublicMember(E:TPasElement):boolean;
|
|
34
|
+
begin Result:=E.Visibility in [visDefault,visPublic,visPublished] end;
|
|
35
|
+
function Node(E:TPasElement):TJSONObject;
|
|
36
|
+
var A,B:TJSONArray; C:TPasClassType; P:TPasProperty; I:integer; M:TPasElement; O:TJSONObject;
|
|
37
|
+
begin
|
|
38
|
+
Result:=TJSONObject.Create(['name',E.Name,'line',E.SourceLinenumber]);
|
|
39
|
+
if E is TPasClassType then begin
|
|
40
|
+
C:=TPasClassType(E); Result.Add('kind','class'); Result.Add('ancestor',TypeName(C.AncestorType));
|
|
41
|
+
A:=TJSONArray.Create; B:=TJSONArray.Create; Result.Add('properties',A);Result.Add('methods',B);
|
|
42
|
+
for I:=0 to C.Members.Count-1 do begin
|
|
43
|
+
M:=TPasElement(C.Members[I]); if not PublicMember(M) then continue;
|
|
44
|
+
if M is TPasProperty then begin
|
|
45
|
+
P:=TPasProperty(M);
|
|
46
|
+
O:=TJSONObject.Create(['name',P.Name,'type',TypeName(P.VarType),'indexed',P.Args.Count>0,
|
|
47
|
+
'readable',Assigned(P.ReadAccessor) or (P.ReadAccessorName<>''),'writable',Assigned(P.WriteAccessor) or (P.WriteAccessorName<>''),
|
|
48
|
+
'default',P.DefaultValue,'line',P.SourceLinenumber]); A.Add(O);
|
|
49
|
+
end else if M is TPasProcedure then begin
|
|
50
|
+
O:=TJSONObject.Create(['name',M.Name,'result','','arguments',TPasProcedure(M).ProcType.Args.Count]);
|
|
51
|
+
if TPasProcedure(M).ProcType is TPasFunctionType then
|
|
52
|
+
O.Strings['result']:=TypeName(TPasFunctionType(TPasProcedure(M).ProcType).ResultEl.ResultType);
|
|
53
|
+
B.Add(O);
|
|
54
|
+
end;
|
|
55
|
+
end;
|
|
56
|
+
end else if E is TPasEnumType then begin
|
|
57
|
+
Result.Add('kind','enum'); A:=TJSONArray.Create;Result.Add('values',A);
|
|
58
|
+
for I:=0 to TPasEnumType(E).Values.Count-1 do begin
|
|
59
|
+
M:=TPasElement(TPasEnumType(E).Values[I]);
|
|
60
|
+
A.Add(TJSONObject.Create(['name',M.Name,'assigned',TPasEnumValue(M).AssignedValue]));
|
|
61
|
+
end;
|
|
62
|
+
end else if E is TPasAliasType then begin
|
|
63
|
+
Result.Add('kind','alias');Result.Add('target',TypeName(TPasAliasType(E).DestType));
|
|
64
|
+
end else if E is TPasRangeType then begin
|
|
65
|
+
Result.Add('kind','range');Result.Add('min',TPasRangeType(E).RangeStart);Result.Add('max',TPasRangeType(E).RangeEnd);
|
|
66
|
+
end else if E is TPasType then begin
|
|
67
|
+
Result.Add('kind','unsupported');Result.Add('declarationKind',E.ClassName);
|
|
68
|
+
end else begin Result.Free; Result:=nil end;
|
|
69
|
+
end;
|
|
70
|
+
procedure ParseUnitFile(const N:string);
|
|
71
|
+
var F:string; R:TFileResolver; S:TPascalScanner; P:TPasParser; C:TContainer;
|
|
72
|
+
M:TPasModule; U:TJSONObject; A,B:TJSONArray; I:integer; O:TJSONObject; E:TPasElement;
|
|
73
|
+
begin
|
|
74
|
+
if Seen.IndexOf(LowerCase(N))>=0 then exit;Seen.Add(LowerCase(N));
|
|
75
|
+
F:=Files.Values[LowerCase(N)];if F='' then exit;
|
|
76
|
+
R:=TFileResolver.Create; S:=nil;P:=nil;C:=nil;M:=nil;
|
|
77
|
+
try
|
|
78
|
+
R.BaseDirectory:=ExtractFilePath(F);R.AddIncludePath(IncDir);R.AddIncludePath(ExtractFilePath(F));
|
|
79
|
+
S:=TPascalScanner.Create(R); S.AddDefine('FPC');S.AddDefine('FPK');
|
|
80
|
+
if SameText(ParamStr(3),'win64') then begin S.AddDefine('MSWINDOWS');S.AddDefine('WINDOWS');S.AddDefine('WIN64') end
|
|
81
|
+
else begin S.AddDefine('LINUX');S.AddDefine('UNIX') end;
|
|
82
|
+
S.AddDefine('CPUX86_64');S.AddDefine('CPU64');S.AddMacro('FPC_FULLVERSION','30202');S.AddDefine('VER3_2');S.AddDefine('NOGUI');S.AddDefine('NOREPORT');
|
|
83
|
+
S.OpenFile(F);C:=TContainer.Create;P:=TPasParser.Create(S,R,C);
|
|
84
|
+
P.Options:=P.Options+[po_IgnoreUnknownResource,po_NoOverloadedProcs,po_AsmWhole];
|
|
85
|
+
P.ParseMain(M);Modules.Add(M);
|
|
86
|
+
U:=TJSONObject.Create(['name',M.Name,'file',Copy(F,Length(Root)+2,MaxInt)]);
|
|
87
|
+
A:=TJSONArray.Create;B:=TJSONArray.Create;U.Add('uses',A);U.Add('types',B);
|
|
88
|
+
for I:=0 to High(M.InterfaceSection.UsesClause) do begin
|
|
89
|
+
E:=M.InterfaceSection.UsesClause[I]; A.Add(E.Name);
|
|
90
|
+
if not SameText(E.Name,'ACBrBase') and not (Pos('ACBrUtil.',E.Name)=1) then Queue.Add(E.Name);
|
|
91
|
+
end;
|
|
92
|
+
for I:=0 to M.InterfaceSection.Types.Count-1 do begin
|
|
93
|
+
O:=Node(TPasElement(M.InterfaceSection.Types[I]));if O<>nil then B.Add(O);
|
|
94
|
+
end;
|
|
95
|
+
for I:=0 to M.InterfaceSection.Classes.Count-1 do begin
|
|
96
|
+
O:=Node(TPasElement(M.InterfaceSection.Classes[I]));if O<>nil then B.Add(O);
|
|
97
|
+
end;
|
|
98
|
+
Units.Add(U);
|
|
99
|
+
except on Ex:Exception do begin
|
|
100
|
+
WriteLn(StdErr,'Cannot parse ',F,': ',Ex.Message);Halt(2);
|
|
101
|
+
end end;
|
|
102
|
+
P.Free;S.Free;C.Free;R.Free;
|
|
103
|
+
end;
|
|
104
|
+
var I:integer; Output:TJSONObject; OutText:TStringList;
|
|
105
|
+
begin
|
|
106
|
+
if ParamCount<2 then begin WriteLn(StdErr,'Usage: extract-model SOURCE_ROOT OUTPUT_JSON');Halt(1) end;
|
|
107
|
+
Root:=ExcludeTrailingPathDelimiter(ExpandFileName(ParamStr(1)));
|
|
108
|
+
IncDir:=Root+'/Fontes/ACBrComum';
|
|
109
|
+
Files:=TStringList.Create;Files.CaseSensitive:=false;Queue:=TStringList.Create;
|
|
110
|
+
Seen:=TStringList.Create;Modules:=TObjectList.Create(true);Units:=TJSONArray.Create;
|
|
111
|
+
try
|
|
112
|
+
IndexDir(Root+'/Fontes');Queue.Add('ACBrNFe.Classes');I:=0;
|
|
113
|
+
while I<Queue.Count do begin ParseUnitFile(Queue[I]);Inc(I) end;
|
|
114
|
+
if Units.Count=0 then raise Exception.Create('ACBrNFe.Classes source was not found');
|
|
115
|
+
Output:=TJSONObject.Create(['schemaVersion',1,'root','TNFe']);Output.Add('units',Units);
|
|
116
|
+
OutText:=TStringList.Create;OutText.Text:=Output.FormatJSON;OutText.SaveToFile(ParamStr(2));
|
|
117
|
+
OutText.Free;Output.Free;
|
|
118
|
+
finally Modules.Free;Seen.Free;Queue.Free;Files.Free end;
|
|
119
|
+
end.
|