1 {
2  ***************************************************************************
3  *                                                                         *
4  *   This source is free software; you can redistribute it and/or modify   *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This code is distributed in the hope that it will be useful, but      *
10  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
12  *   General Public License for more details.                              *
13  *                                                                         *
14  *   A copy of the GNU General Public License is available on the World    *
15  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
16  *   obtain it by writing to the Free Software Foundation,                 *
17  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
18  *                                                                         *
19  ***************************************************************************
20 
21  Author: Balázs Székely
22  Abstract:
23    Common functions, procedures.
24 }
25 unit opkman_common;
26 
27 {$mode objfpc}{$H+}
28 
29 interface
30 
31 uses
32   Classes, SysUtils, contnrs,
33   // LCL
34   Dialogs, Forms, Controls,
35   // LazUtils
36   LazFileUtils,
37   // IdeIntf
38   LazIDEIntf, PackageIntf,
39   // OpkMan
40   opkman_const, opkman_options;
41 
42 type
43   TPackageAction = (paDownloadTo, paInstall, paUpdate);
44 
45   TPackageData = class(TObject)
46     FName: String;
47     FPackageBaseDir: String;
48     FPackageRelativePath: String;
49     FFullPath: String;
50   end;
51 
52 const
53   MaxCategories = 28;
54   Categories: array[0..MaxCategories - 1] of String = (
55     rsMainFrm_VSTText_PackageCategory0,
56     rsMainFrm_VSTText_PackageCategory1,
57     rsMainFrm_VSTText_PackageCategory2,
58     rsMainFrm_VSTText_PackageCategory3,
59     rsMainFrm_VSTText_PackageCategory4,
60     rsMainFrm_VSTText_PackageCategory5,
61     rsMainFrm_VSTText_PackageCategory6,
62     rsMainFrm_VSTText_PackageCategory7,
63     rsMainFrm_VSTText_PackageCategory8,
64     rsMainFrm_VSTText_PackageCategory9,
65     rsMainFrm_VSTText_PackageCategory10,
66     rsMainFrm_VSTText_PackageCategory11,
67     rsMainFrm_VSTText_PackageCategory12,
68     rsMainFrm_VSTText_PackageCategory13,
69     rsMainFrm_VSTText_PackageCategory14,
70     rsMainFrm_VSTText_PackageCategory15,
71     rsMainFrm_VSTText_PackageCategory16,
72     rsMainFrm_VSTText_PackageCategory17,
73     rsMainFrm_VSTText_PackageCategory18,
74     rsMainFrm_VSTText_PackageCategory19,
75     rsMainFrm_VSTText_PackageCategory20,
76     rsMainFrm_VSTText_PackageCategory21,
77     rsMainFrm_VSTText_PackageCategory22,
78     rsMainFrm_VSTText_PackageCategory23,
79     rsMainFrm_VSTText_PackageCategory24,
80     rsMainFrm_VSTText_PackageCategory25,
81     rsMainFrm_VSTText_PackageCategory26,
82     rsMainFrm_VSTText_PackageCategory27);
83   //needed for localized filter, since the JSON contains only english text
84   CategoriesEng: array[0..MaxCategories - 1] of String = (
85     'Charts and Graphs',
86     'Cryptography',
87     'DataControls',
88     'Date and Time',
89     'Dialogs',
90     'Edit and Memos',
91     'Files and Drives',
92     'GUIContainers',
93     'Graphics',
94     'Grids',
95     'Indicators and Gauges',
96     'Labels',
97     'LazIDEPlugins',
98     'List and Combo Boxes',
99     'ListViews and TreeViews',
100     'Menus',
101     'Multimedia',
102     'Networking',
103     'Panels',
104     'Reporting',
105     'Science',
106     'Security',
107     'Shapes',
108     'Sizers and Scrollers',
109     'System',
110     'Tabbed Components',
111     'Other',
112     'Games and Game Engines');
113 
114 var
115   LocalRepositoryConfigFile: String;
116   LocalRepositoryUpdatesFile: String;
117   PackageAction: TPackageAction;
118   InstallPackageList: TObjectList;
119 
MessageDlgExnull120 function MessageDlgEx(const AMsg: String; ADlgType: TMsgDlgType;  AButtons:
121   TMsgDlgButtons; AParent: TForm): TModalResult;
122 procedure InitLocalRepository;
SecToHourAndMinnull123 function SecToHourAndMin(const ASec: LongInt): String;
FormatSizenull124 function FormatSize(Size: Int64): String;
FormatSpeednull125 function FormatSpeed(Speed: LongInt): String;
GetPackageTypeStringnull126 function GetPackageTypeString(aPackageType: TLazPackageType): String;
GetDirSizenull127 function GetDirSize(const ADirName: String; var AFileCnt, ADirCnt: Integer): Int64;
128 procedure FindPackages(const ADirName: String; APackageList: TStrings);
129 procedure FindAllFilesEx(const ADirName: String; AFileList: TStrings);
FixProtocolnull130 function FixProtocol(const AURL: String): String;
IsDirectoryEmptynull131 function IsDirectoryEmpty(const ADirectory: String): Boolean;
132 
133 implementation
134 
MessageDlgExnull135 function MessageDlgEx(const AMsg: string; ADlgType: TMsgDlgType;
136   AButtons: TMsgDlgButtons; AParent: TForm): TModalResult;
137 var
138   MsgFrm: TForm;
139 begin
140   MsgFrm := CreateMessageDialog(AMsg, ADlgType, AButtons);
141   try
142     MsgFrm.FormStyle := fsSystemStayOnTop;
143     if AParent <> nil then
144     begin
145       MsgFrm.Position := poDefaultSizeOnly;
146       MsgFrm.Left := AParent.Left + (AParent.Width - MsgFrm.Width) div 2;
147       MsgFrm.Top := AParent.Top + (AParent.Height - MsgFrm.Height) div 2;
148     end
149     else
150       MsgFrm.Position := poWorkAreaCenter;
151     Result := MsgFrm.ShowModal;
152   finally
153     MsgFrm.Free
154   end;
155 end;
156 
157 procedure InitLocalRepository;
158 var
159   LocalRepo, LocalRepoConfig: String;
160 begin
161   LocalRepo := AppendPathDelim(AppendPathDelim(LazarusIDE.GetPrimaryConfigPath) + cLocalRepository);
162   if not DirectoryExists(LocalRepo) then
163     CreateDir(LocalRepo);
164 
165   LocalRepoConfig := AppendPathDelim(LocalRepo + cLocalRepositoryConfig);
166   if not DirectoryExists(LocalRepoConfig) then
167     CreateDir(LocalRepoConfig);
168   LocalRepositoryConfigFile := LocalRepoConfig + cLocalRepositoryConfigFile;
169   LocalRepositoryUpdatesFile := LocalRepoConfig + cLocalRepositoryUpdatesFile;
170 end;
171 
SecToHourAndMinnull172 function SecToHourAndMin(const ASec: LongInt): String;
173 var
174   Hour, Min, Sec: LongInt;
175 begin
176    Hour := Trunc(ASec/3600);
177    Min  := Trunc((ASec - Hour*3600)/60);
178    Sec  := ASec - Hour*3600 - 60*Min;
179    Result := IntToStr(Hour) + 'h: ' + IntToStr(Min) + 'm: ' + IntToStr(Sec) + 's';
180 end;
181 
FormatSizenull182 function FormatSize(Size: Int64): String;
183 const
184   KB = 1024;
185   MB = 1024 * KB;
186   GB = 1024 * MB;
187 begin
188   if Size < KB then
189     Result := FormatFloat('#,##0 Bytes', Size)
190   else if Size < MB then
191     Result := FormatFloat('#,##0.0 KB', Size / KB)
192   else if Size < GB then
193     Result := FormatFloat('#,##0.0 MB', Size / MB)
194   else
195     Result := FormatFloat('#,##0.0 GB', Size / GB);
196 end;
197 
FormatSpeednull198 function FormatSpeed(Speed: LongInt): String;
199 const
200   KB = 1024;
201   MB = 1024 * KB;
202   GB = 1024 * MB;
203 begin
204   if Speed < KB then
205     Result := FormatFloat('#,##0 bits/s', Speed)
206   else if Speed < MB then
207     Result := FormatFloat('#,##0.0 kB/s', Speed / KB)
208   else if Speed < GB then
209     Result := FormatFloat('#,##0.0 MB/s', Speed / MB)
210   else
211     Result := FormatFloat('#,##0.0 GB/s', Speed / GB);
212 end;
213 
GetPackageTypeStringnull214 function GetPackageTypeString(aPackageType: TLazPackageType): String;
215 begin
216   case aPackageType of
217     lptRunAndDesignTime: Result := rsMainFrm_VSTText_PackageType0;
218     lptDesignTime:       Result := rsMainFrm_VSTText_PackageType1;
219     lptRunTime:          Result := rsMainFrm_VSTText_PackageType2;
220     lptRunTimeOnly:      Result := rsMainFrm_VSTText_PackageType3;
221   end;
222 end;
223 
GetDirSizenull224 function GetDirSize(const ADirName: String; var AFileCnt, ADirCnt: Integer): Int64;
225 var
226   DirSize: Int64;
227 
228   procedure GetSize(const ADirName: String);
229   var
230     SR: TSearchRec;
231     DirName: String;
232   begin
233     DirName := AppendPathDelim(ADirName);
234     if FindFirst(DirName + '*', faAnyFile - faDirectory, SR) = 0 then
235     begin
236       try
237         repeat
238           Inc(AFileCnt);
239           DirSize:= DirSize + SR.Size;
240         until FindNext(SR) <> 0;
241       finally
242         FindClose(SR);
243       end;
244     end;
245     if FindFirst(DirName + '*', faAnyFile, SR) = 0 then
246     begin
247       try
248         repeat
249           if ((SR.Attr and faDirectory) <> 0)  and (SR.Name <> '.') and (SR.Name <> '..') then
250            begin
251              Inc(ADirCnt);
252              GetSize(DirName + SR.Name);
253            end;
254         until FindNext(SR) <> 0;
255       finally
256         FindClose(SR);
257       end;
258     end;
259   end;
260 begin
261   DirSize := 0;
262   AFileCnt := 0;
263   ADirCnt := 0;
264   GetSize(ADirName);
265   Result := DirSize;
266 end;
267 
268 procedure FindPackages(const ADirName: String; APackageList: TStrings);
269 var
270   BaseDir, BasePath: String;
271   SLExcludedFolders: TStringList;
272 
IsAllowednull273   function IsAllowed(AName: String): Boolean;
274   var
275     I: Integer;
276   begin
277     Result := True;
278     for I := 0 to SLExcludedFolders.Count - 1 do
279     begin
280       if UpperCase(SLExcludedFolders.Strings[I]) = UpperCase(AName) then
281         begin
282           Result := False;
283           Break;
284         end;
285     end;
286   end;
287 
288   procedure FindFiles(const ADirName: String);
289   var
290     SR: TSearchRec;
291     Path: String;
292     PackageData: TPackageData;
293   begin
294     Path := AppendPathDelim(ADirName);
295     if FindFirst(Path + '*', faAnyFile - faDirectory, SR) = 0 then
296     begin
297       try
298         repeat
299           if (UpperCase(ExtractFileExt(SR.Name)) = UpperCase('.lpk')) then
300           begin
301             PackageData := TPackageData.Create;
302             PackageData.FName := SR.Name;
303             PackageData.FPackageBaseDir := BaseDir;
304             PackageData.FPackageRelativePath := StringReplace(Path, BasePath, '', [rfIgnoreCase, rfReplaceAll]);
305             if Trim(PackageData.FPackageRelativePath) <> '' then
306             begin
307               if PackageData.FPackageRelativePath[Length(PackageData.FPackageRelativePath)] = PathDelim then
308                 PackageData.FPackageRelativePath := Copy(PackageData.FPackageRelativePath, 1, Length(PackageData.FPackageRelativePath) - 1);
309               PackageData.FPackageRelativePath := PackageData.FPackageRelativePath;
310             end;
311             PackageData.FFullPath := Path + SR.Name;
312             APackageList.AddObject(PackageData.FName, PackageData);
313           end;
314         until FindNext(SR) <> 0;
315       finally
316         FindClose(SR);
317       end;
318     end;
319 
320     if FindFirst(Path + '*', faAnyFile, SR) = 0 then
321     begin
322       try
323         repeat
324           if ((SR.Attr and faDirectory) <> 0) and (SR.Name <> '.') and (SR.Name <> '..') then
325             if IsAllowed(SR.Name) then
326               FindFiles(Path + SR.Name);
327         until FindNext(SR) <> 0;
328       finally
329         FindClose(SR);
330       end;
331     end;
332   end;
333 
334 begin
335   BasePath := AppendPathDelim(ADirName);
336   if ADirName[Length(ADirName)] = PathDelim then
337     BaseDir := ExtractFileName(Copy(ADirName, 1, Length(ADirName) - 1))
338   else
339     BaseDir := ExtractFileName(ADirName);
340 
341   SLExcludedFolders := TStringList.Create;
342   try
343     SLExcludedFolders.Delimiter := ',';
344     SLExcludedFolders.StrictDelimiter := True;
345     SLExcludedFolders.DelimitedText := Options.ExcludedFolders;
346     FindFiles(ADirName);
347   finally
348     SLExcludedFolders.Free;
349   end;
350 end;
351 
352 procedure FindAllFilesEx(const ADirName: String; AFileList: TStrings);
353 var
354   SLExcludedFiles: TStringList;
355   SLExcludedFolders: TStringList;
356 
IsAllowednull357   function IsAllowed(const AName: String; const AIsDir: Boolean): Boolean;
358   var
359     I: Integer;
360   begin
361     Result := True;
362     if not AIsDir then
363     begin
364       for I := 0 to SLExcludedFiles.Count - 1 do
365       begin
366         if UpperCase(SLExcludedFiles.Strings[I]) = UpperCase(ExtractFileExt(AName)) then
367         begin
368           Result := False;
369           Break;
370         end;
371       end;
372     end
373     else
374     begin
375       for I := 0 to SLExcludedFolders.Count - 1 do
376         if UpperCase(SLExcludedFolders.Strings[I]) = UpperCase(AName) then
377         begin
378           Result := False;
379           Break;
380         end;
381     end;
382   end;
383 
384   procedure FindFiles(const ADirName: String);
385   var
386     SR: TSearchRec;
387     Path: String;
388   begin
389     Path := AppendPathDelim(ADirName);
390     if FindFirst(Path + '*', faAnyFile - faDirectory, SR) = 0 then
391     begin
392       try
393         repeat
394            if IsAllowed(SR.Name, False) then
395              AFileList.Add(Path + SR.Name);
396         until FindNext(SR) <> 0;
397       finally
398         FindClose(SR);
399       end;
400     end;
401 
402     if FindFirst(Path + '*', faAnyFile, SR) = 0 then
403     begin
404       try
405         repeat
406           if ((SR.Attr and faDirectory) <> 0) and (SR.Name <> '.') and (SR.Name <> '..') then
407             if IsAllowed(SR.Name, True) then
408               FindFiles(Path + SR.Name);
409         until FindNext(SR) <> 0;
410       finally
411         FindClose(SR);
412       end;
413     end;
414   end;
415 
416 var
417   I, P: Integer;
418   Ext: String;
419 begin
420   SLExcludedFiles := TStringList.Create;
421   try
422     SLExcludedFiles.Delimiter := ',';
423     SLExcludedFiles.StrictDelimiter := True;
424     SLExcludedFiles.DelimitedText := Options.ExcludedFiles;
425     for I := 0 to SLExcludedFiles.Count - 1 do
426     begin
427       P := Pos('*.', SLExcludedFiles.Strings[I]);
428       if P > 0 then
429       begin
430         Ext := Copy(SLExcludedFiles.Strings[I], 2, Length(SLExcludedFiles.Strings[I]));
431         if Trim(Ext) = '.' then
432           Ext := '';
433       end
434       else
435         Ext := '.' + SLExcludedFiles.Strings[I];
436       SLExcludedFiles.Strings[I] := Ext;
437     end;
438     SLExcludedFolders := TStringList.Create;
439     try
440       SLExcludedFolders.Delimiter := ',';
441       SLExcludedFolders.StrictDelimiter := True;
442       SLExcludedFolders.DelimitedText := Options.ExcludedFolders;
443       FindFiles(ADirName);
444     finally
445       SLExcludedFolders.Free;
446     end;
447   finally
448     SLExcludedFiles.Free;
449   end;
450 end;
451 
FixProtocolnull452 function FixProtocol(const AURL: String): String;
453 begin
454   Result := AURL;
455   if (Pos('http://', Result) = 0) and (Pos('https://', Result) = 0) then
456     Result := 'https://' + Result;
457 end;
458 
IsDirectoryEmptynull459 function IsDirectoryEmpty(const ADirectory: String): Boolean;
460 var
461   SearchRec: TSearchRec;
462   SearchRes: Longint;
463 begin
464   Result := true;
465   SearchRes := FindFirst(IncludeTrailingPathDelimiter(ADirectory) + AllFilesMask, faAnyFile, SearchRec);
466   try
467     while SearchRes = 0 do
468     begin
469       if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then
470       begin
471         Result := False;
472         Break;
473       end;
474       SearchRes := FindNext(SearchRec);
475     end;
476   finally
477     SysUtils.FindClose(SearchRec);
478   end;
479 end;
480 
481 end.
482 
483