root/trunk/ExtraFileUtilsLCL.pas

Revision 130, 28.7 kB (checked in by sagrer, 1 year ago)
  1. Пофиксан баг с опечатков в генерируемых pas и h
  2. Пофиксан баг в SourcePackBuilder? который был в функции копирования директории в ExtraFileUtilsLCL
  • Property svnmailer:content-charset set to cp1251
Line 
1 ///////////////////////////////////////////////////////////
2 //                   ExtraFileUtilsLCL                   //
3 //    Модуль с самопальными функциями и прочим общего    //
4 //              для работы с файлами под LCL.            //
5 //             Copyright (C) 2007 Gipat Group            //
6 //              Распространяется на условиях             //
7 //                      LGPL v2.1                        //
8 //                                                       //
9 //                  www.gipatgroup.org                   //
10 ///////////////////////////////////////////////////////////
11
12 //К работе над данным файлом приложили руки, ноги.... короче аффтары:
13 // 1) Immortal (immortalGAD@rambler.ru)
14 // 2) Sagrer (sagrer@yandex.ru)
15
16 ////////////////////////////////////////////////////////////////////////
17
18 unit ExtraFileUtilsLCL;
19
20 {$mode objfpc}{$H+}
21
22 interface
23
24 uses
25   windows ,Classes, SysUtils, FileUtil, ExtraFunctionsLcl, syncobjs;
26
27 const
28 // Размер буффера, который будет использоватсья для копирования файлов
29   FILECOPY_BUFFER_SIZE = 81920;
30 // Нереальное значение аттрибута
31   INVALID_SYSFILE_ATTR :LongInt = -1;
32  
33 // возрвращаемые значения
34   resOk = 0;
35   resParametersError = 1;
36 /////////////////////////
37  
38 type
39 // таки пока только для внутреннего пользования.
40   IOException = class(Exception) end;
41   TFileSystemIterator = class;
42  
43 ////////////////////////////////////////////////////////////////////////////////
44 // Прототип процедуры, для информирования о ходе копирования
45   TCopyFileProgressCallback = procedure(const current, total :Int64) of object;
46 // Прототип процедуры, для информирования о завершении
47   TCopyFileFinishCallback   = procedure(const resultt :Boolean) of object;
48
49 // класс для асинхронного копирования файла.
50   TCopyFileThread = class (TThread)
51     private
52       curLock, totalLock :TCriticalSection;
53       _current, _total :Int64;
54     protected
55       procedure Execute; override;
56       function  GetTotal   :Int64;
57       procedure SetTotal  (const value :Int64);
58       function  GetCurrent :Int64;
59       procedure SetCurrent(const value :Int64);
60     public
61       // результат операции
62       CopyResult :Boolean;
63       constructor Create(const source, dest :AnsiString; const overwriteExists , startSuspended :Boolean);
64       destructor Destroy; override;
65       CopyFileProgress :TCopyFileProgressCallback;
66       CopyFileFinish   :TCopyFileFinishCallback;
67       SourceFilePath   :AnsiString;
68       DestFilePath     :AnsiString;
69       OverwriteExists  :Boolean;  // переписывать файл , если уже существует
70       property Total   :Int64 read GetTotal;
71       property Current :Int64 read GetCurrent;
72   end;
73
74 ////////////////////////////////////////////////////////////////////////////////
75 //  базовый класс, для представления файла или папки
76   TFileSystemElemInfo = class
77     protected
78       SearchRec :TSearchRec;
79     public
80       // абсолютный путь к файлу или папке
81       FullPath :AnsiString;
82       constructor Create(const filePath :AnsiString); virtual;
83       constructor Create(const filePath: AnsiString; const searchInfo :TSearchRec); virtual;
84       function  Name :AnsiString; virtual;
85       function  Attributes :Longint; virtual;
86       function  Exists :Boolean; virtual; abstract;
87       function  CopyTo(const path :AnsiString; const overwriteExists :Boolean = false) :Boolean; virtual; abstract;
88       function  Delete :Boolean; virtual ;abstract;
89   end;
90
91 ////////////////////////////////////////////////////////////////////////////////
92 //  класс для работы с файлами
93   TFileInfo = class (TFileSystemElemInfo)
94     public
95       function CopyTo(const path :AnsiString; const overwriteExists :Boolean = false) :Boolean; override;
96       function Delete :Boolean; override;
97       function Exists :Boolean; override;
98   end;
99  
100 ////////////////////////////////////////////////////////////////////////////////
101 //  класс для работы с директорией
102   TDirectoryInfo = class (TFileSystemElemInfo)
103     public
104       function CopyTo(const path :AnsiString; const overwriteExists :Boolean = false) :Boolean; override;
105       function Delete :Boolean; override;
106       function Exists :Boolean; override;
107       function GetFiles :TFileSystemIterator;
108       function GetFiles(const mask :AnsiString) :TFileSystemIterator;
109       function GetDirectories :TFileSystemIterator;
110       function GetDirectories(const mask :AnsiString) :TFileSystemIterator;
111   end;
112  
113 ////////////////////////////////////////////////////////////////////////////////
114 // класс для перебора файлов и поддирекорий в директории
115   TFileSystemIterator = class
116     protected
117       searchInfo :TSearchRec;
118       folder     :AnsiString;
119     public
120       Current    :TFileSystemElemInfo;  // текущий файл\папка
121       SearchAttr :Longint;
122       constructor Create(const folderPath :AnsiString; const onlyFiles :Boolean = false);
123       constructor Create(const folderPath :AnsiString; const attrPredicat :Longint);
124       constructor Create(const folderPath, searchMask :AnsiString; const onlyFiles :Boolean = false);
125       constructor Create(const folderPath, searchMask :AnsiString; const attrPredicat :Longint); //
126       destructor  Destroy; override;
127       function Next :Boolean;
128   end;
129
130 // Класс для работы с временными папками. Чистит temp по завершению работы.
131   TTempFolder = class
132     protected
133       path :AnsiString;
134       name :AnsiString;
135     public
136       constructor Create(const subName :AnsiString);
137       destructor  Destroy; override;
138       function GetTempPath :AnsiString;   //получить путь к временной директории
139   end;
140  
141 ////////////////////////////////////////////////////////////////////////////////
142 //  Процедуры и функции
143 ////////////////////////////////////////////////////////////////////////////////
144
145 // копирует дирректорию
146 function CopyDirectory(const sourceDir, destDir :AnsiString; const overwriteExists :Boolean = false;
147                         const recursive :Boolean = true) :Boolean;
148
149
150 //AsyncCopyFile - копирует файл в отдельном потоке
151 function AsyncCopyFile(const sourcePath, destPath :AnsiString; const onFinish :TCopyFileFinishCallback;
152                         const overwriteExists :Boolean = false) :TThread;
153 function AsyncCopyFile(const sourcePath, destPath :AnsiString; const onFinish :TCopyFileFinishCallback;
154                         const onProgress :TCopyFileProgressCallback; const overwriteExists :Boolean = false) :TThread;
155
156 // SysFileExists - проверяет существует ли по указанному пути файл или папка
157 function FileSystemElemExists(const path :AnsiString) :Boolean;
158
159 // IsDirectory - проверяет по пути или атрибутам является или "это" папкой
160 function IsDirectory(const path :AnsiString) :Boolean;
161 function IsDirectory(const attr :Longint) :Boolean;
162
163 Function IsLocalPath(const PathStr : AnsiString) : boolean;     //Возвращает true если путь локальный, и false если глобальный. Под локальным понимается путь начинающийся с точки.
164 Function IsGlobalPath(const PathStr : AnsiString) : boolean;   //Возвращает true если путь глобальный. Под глобальным понимается путь, начинающийся на имя диска (функция для вендов).
165 Function FixLocalPath(const PathStr : AnsiString) : AnsiString;   //Правильно оформляет предположительно локальный но неправильно оформленный путь - чтоб была точка в начале.
166 Function ResolveLocalPath(const LocalPath, GlobalPath : AnsiString) : AnsiString;  //Превратить относительный путь в абсолютный
167 Function GetLocalPath(const GlobalRoot, FullPath : AnsiString): AnsiString;    //Вернуть относительный путь.
168 Function GetFileSize(const FileName : AnsiString) : Int64;   //Получить размер файла.
169 function GetFilesNumber(const DirName : AnsiString; const Recursive : boolean) : LongInt;//Подсчитать количество файлов и папок в папке...
170 Function IsDirsChild(const DirName, ChildName : AnsiString) : boolean;   //Проверить - принадлежит ли путь ChildName пути DirName.
171 function AddExtensionIfAbsent(const path, extension :String) :String; // добаляет расширение если его еще нет.
172 function GetFileNameWithoutExtension(const path :String) :String;  // чистое имя файла, без расширения
173 Function HasLastSlash(const Str : string) : boolean;    //Функа для проверки наличия последнего слэша.
174 Function AddLastSlash(const str : string) : string;     //Функа для добавления в путь последнего слэша (если надо).
175 Function DelLastSlash(const Str : string) : string;     //Удаляет последний слеш если он есть.
176
177
178 implementation
179
180
181 //////////////////////////  TFileSystemIterator     ///////////////////////////////
182 constructor TFileSystemIterator.Create(const folderPath :AnsiString; const onlyFiles :Boolean = false);
183 begin
184   Create(folderPath, AllDirectoryEntriesMask, onlyFiles);
185 end;
186
187 constructor TFileSystemIterator.Create(const folderPath :AnsiString; const attrPredicat :Longint);
188 begin
189   Create(folderPath, AllDirectoryEntriesMask, attrPredicat);
190 end;
191
192 constructor TFileSystemIterator.Create(const folderPath, searchMask :AnsiString; const onlyFiles :Boolean = false);
193 var
194   temp :Longint;
195 begin
196   temp := faAnyFile;
197   if onlyFiles then begin
198     temp := temp and not faDirectory;
199   end;
200   Create(folderPath, searchMask, temp);
201 end;
202
203 constructor TFileSystemIterator.Create(const folderPath, searchMask :AnsiString; const attrPredicat :Longint);
204 begin
205   inherited Create;
206   Self.SearchAttr := attrPredicat;
207   Self.folder := folderPath;
208   FindFirst(AppendPathDelim(folderPath) + searchMask, faAnyFile, searchInfo);
209 end;
210
211 destructor TFileSystemIterator.Destroy;
212 begin
213   inherited;
214   FindClose(searchInfo);
215 end;
216
217 function TFileSystemIterator.Next :Boolean;
218 begin
219   Result := (FindNext(searchInfo) = 0);
220   if not Result then exit;
221  
222   if (searchInfo.Name = '.') or (searchInfo.Name = '..') then begin
223     Result := Self.Next;
224     exit;
225   end;
226  
227   if (searchInfo.Attr and SearchAttr) = 0 then begin
228     Result := Self.Next;
229     exit;
230   end;
231
232   if IsDirectory(searchInfo.Attr) then
233     Current := TDirectoryInfo.Create(folder, searchInfo)
234   else
235     Current := TFileInfo.Create(folder, searchInfo);
236
237 end;
238
239 ///////////////////              TCopyFileThread             ///////////////////
240
241 //  конструктор
242 constructor TCopyFileThread.Create(const source, dest :AnsiString; const overwriteExists, startSuspended :Boolean);
243 begin
244   inherited Create(startSuspended);
245   SourceFilePath := source;
246   DestFilePath   := dest;
247   self.OverwriteExists := overwriteExists;
248   self._current := 0;
249   Self._total := -1;
250   totalLock := TCriticalSection.Create;
251   curLock   := TCriticalSection.Create;
252 end;
253
254 destructor TCopyFileThread.Destroy;
255 begin
256   inherited;
257   totalLock.Free;
258   curLock.Free;
259 end;
260
261 //  процедура , копирует файл.
262 procedure TCopyFileThread.Execute;
263 var
264   sourceStream :TFileStream;
265   destStream   :TFileStream;
266   curr, max    :Int64;
267   stepReaded   :Integer;
268   part, temp   :Int64;
269   buffer       :array[0 .. FILECOPY_BUFFER_SIZE - 1] of byte;
270 begin
271   CopyResult := false;
272  
273   if not FileExists(SourceFilePath) then begin
274     if assigned (CopyFileFinish) then CopyFileFinish(false);
275     exit;
276   end;
277  
278   // Проверяем сущестует ли файл который мы собираемся создать. Если да - то если разрешенно пробуем удалить,
279   // в случае неудачи выходим.
280   if FileExists(DestFilePath) then begin
281     if (not OverwriteExists) or (not DeleteFile(DestFilePath)) then begin
282       if assigned (CopyFileFinish) then CopyFileFinish(false);
283       exit;
284     end;
285   end;
286  
287   // открываем потоки
288   sourceStream := TFileStream.Create(SourceFilePath, fmOpenRead);
289   destStream   := TFileStream.Create(DestFilePath, fmCreate);
290  
291   curr := 0;
292   max := sourceStream.Size;
293   SetTotal(max);
294   part := total div 100;
295   temp := 0;
296
297   try
298     while (curr < max) do begin
299       stepReaded := sourceStream.Read(buffer, FILECOPY_BUFFER_SIZE);
300       destStream.Write(buffer, stepReaded);
301       curr := curr + stepReaded;
302       SetCurrent(curr);
303       // будем сообщать о прогрессе по окончанию копирования каждой 100-й части,
304       // чтобы не перегружать поток вызовами.
305       if assigned(CopyFileProgress) then begin
306         temp := temp + stepReaded;
307         if temp >= part then begin
308           temp := temp mod part;
309           CopyFileProgress(curr, max);
310         end;
311       end;
312     end;
313   finally
314     sourceStream.Free;
315     destStream.Free;
316   end;
317  
318   CopyResult := true;
319   if assigned (CopyFileFinish) then CopyFileFinish(CopyResult);
320
321 end;
322
323
324 function TCopyFileThread.GetTotal :Int64;
325 begin
326   totalLock.Acquire;
327   try
328     Result := _total;
329   finally
330     totalLock.Release;
331   end;
332 end;
333
334 procedure TCopyFileThread.SetTotal(const value :Int64);
335 begin
336   totalLock.Acquire;
337   try
338     _total := value;
339   finally
340     totalLock.Release;
341   end;
342 end;
343
344 function TCopyFileThread.GetCurrent :Int64;
345 begin
346   curLock.Acquire;
347   try
348     Result := _current;
349   finally
350     curLock.Release;
351   end;
352 end;
353
354 procedure TCopyFileThread.SetCurrent(const value :Int64);
355 begin
356   curLock.Acquire;
357   try
358     _current := value;
359   finally
360     curLock.Release;
361   end;
362 end;
363
364 ///////////////////////  TFileSystemIterator  & co    /////////////////////////////
365
366 // конструктор
367 constructor TFileSystemElemInfo.Create(const filePath :AnsiString);
368 begin
369   inherited Create;
370   Self.FullPath := filePath;
371 end;
372
373 // конструктор
374 constructor TFileSystemElemInfo.Create(const filePath: AnsiString; const searchInfo :TSearchRec);
375 begin
376   Create(AppendPathDelim(filePath) + searchInfo.Name);
377   Self.SearchRec := searchInfo;
378 end;
379
380 // взять аттрибут
381 function TFileSystemElemInfo.Attributes :Longint;
382 begin
383   // если определена структура SearchRec
384   if (SearchRec.FindHandle = 0) then
385     Result := INVALID_SYSFILE_ATTR
386   else
387     Result := SearchRec.Attr;
388 end;
389
390 // извлечь имя из пути
391 function TFileSystemElemInfo.Name :AnsiString;
392 begin
393   Result := SearchRec.Name;
394   if (Result = '') then
395     Result := ExtractFileName(FullPath);
396 end;
397
398 // копает
399 function TFileInfo.CopyTo(const path :AnsiString; const overwriteExists :Boolean = false) :Boolean;
400 begin
401   if FileExists(path) then begin
402     if overwriteExists then begin
403       if not DeleteFile(path) then begin
404         Result := false;
405         exit;
406       end;
407     end else begin
408       Result := true;
409       exit;
410     end;
411   end;
412   Result := CopyFile(FullPath, path);
413 end;
414
415 // удаляет
416 function TFileInfo.Delete :Boolean;
417 begin
418   Result := false;
419   if FileExists(FullPath) then
420     Result := DeleteFile(FullPath);
421 end;
422
423 // а есть ли у нас такой файл?
424 function TFileInfo.Exists :Boolean;
425 begin
426   Result := FileExists(FullPath);
427 end;
428
429 // тоже копирует.
430 function TDirectoryInfo.CopyTo(const path :AnsiString; const overwriteExists :Boolean = false) :Boolean;
431 begin
432   Result := CopyDirectory(FullPath, path, overwriteExists, true);
433 end;
434
435 // удаляет нафиг.
436 function TDirectoryInfo.Delete :Boolean;
437 begin
438   Result := DeleteDirectory(FullPath, false);
439 end;
440
441 // есть ли папка
442 function TDirectoryInfo.Exists :Boolean;
443 begin
444   Result := DirectoryExists(FullPath);
445 end;
446
447 // получить итератор на файлы внутри папки
448 function TDirectoryInfo.GetFiles: TFileSystemIterator;
449 begin
450   Result := GetFiles('*');
451 end;
452
453 // получить итереатор на все чайлд-папки
454 function TDirectoryInfo.GetFiles(const mask :AnsiString) :TFileSystemIterator;
455 begin
456   Result := TFileSystemIterator.Create(FullPath, mask, true);
457 end;
458
459 function TDirectoryInfo.GetDirectories :TFileSystemIterator;
460 begin
461   Result := GetDirectories('*');
462 end;
463
464 function TDirectoryInfo.GetDirectories(const mask :AnsiString) :TFileSystemIterator;
465 begin
466   Result := TFileSystemIterator.Create(FullPath, mask, faDirectory);
467 end;
468
469 //////////////////////////  TTempFolder     ///////////////////////////////
470 // конструктор. создает временную директорию
471 constructor TTempFolder.Create(const subName :AnsiString);
472 var
473   temp :TGUID;
474 begin
475   CreateGUID(temp);
476   // собственно путь к папке
477   path := AppendPathDelim(GetTempDir) + GUIDToString(temp) + PathDelim;
478   ForceDirectory(path + subName);
479   name := subName;
480 end;
481
482 // чистит за собой
483 destructor  TTempFolder.Destroy;
484 begin
485   inherited;
486   DeleteDirectory(path, false);
487 end;
488
489 //получить путь к временной директории
490 function TTempFolder.GetTempPath :AnsiString;
491 begin
492   Result := AppendPathDelim(path + name);
493 end;
494
495 ////////////////////////////////////////////////////////////////////////////////
496 // копирует директорию
497 function CopyDirectory(const sourceDir, destDir :AnsiString; const overwriteExists :Boolean = false;
498                         const recursive :Boolean = true) :Boolean;
499 var
500   it :TFileSystemIterator;
501 begin
502   Result := false;
503   if not DirectoryExists(sourceDir) then begin
504     exit;
505   end;
506   // если destDir еще не существует, создаем
507   if not DirectoryExists(destDir) then ForceDirectory(destDir);
508   it := TFileSystemIterator.Create(sourceDir, not recursive);
509   try
510     while it.Next do begin
511       if not it.Current.CopyTo(AppendPathDelim(destDir) + it.Current.Name, overwriteExists) then begin
512         exit;
513       end;
514     end;
515     Result := true;
516   finally
517     it.Free;
518   end;
519 end;
520
521
522 // копирования файла в отдельном потоке
523 function AsyncCopyFile(const sourcePath, destPath :AnsiString; const onFinish :TCopyFileFinishCallback;
524                         const overwriteExists :Boolean = false) :TThread;
525 begin
526   Result := AsyncCopyFile(sourcePath, destPath, onFinish, nil, overwriteExists);
527 end;
528
529 // копирует файл в отдельном поток
530 function AsyncCopyFile(const sourcePath, destPath :AnsiString; const onFinish :TCopyFileFinishCallback;
531                        const onProgress :TCopyFileProgressCallback; const overwriteExists :Boolean = false) :TThread;
532 var
533   worker :TCopyFileThread;
534 begin
535   worker := TCopyFileThread.Create(sourcePath, destPath, overwriteExists ,true);
536   worker.CopyFileProgress := onProgress;
537   worker.CopyFileFinish   := onFinish;
538   worker.FreeOnTerminate  := true;
539   worker.Resume;
540   Result := worker;
541 end;
542
543 function FileSystemElemExists(const path :AnsiString) :Boolean;
544 begin
545   Result := (FileGetAttr(path) <> INVALID_SYSFILE_ATTR);
546 end;
547
548 // а директория ли по переданному пути ?
549 function IsDirectory(const path :AnsiString) :Boolean;
550 var
551   attr :Longint;
552 begin
553   attr := FileGetAttr(path);
554   if attr <> INVALID_SYSFILE_ATTR then begin
555     Result := IsDirectory(attr);
556   end else
557     Result  := false;
558 end;
559
560 // проверят является ли системный файл директорией по аттрибутам
561 function IsDirectory(const attr :Longint) :Boolean;
562 begin
563   Result := (attr and faDirectory) = faDirectory;
564 end;
565
566
567 ////////////////   импорт из ExtraFunctionsCLC  ////////////////////////////////
568 Function IsLocalPath(const PathStr : string) : boolean;
569 //Возвращает true если путь локальный, и false если глобальный.
570 //Под локальным понимается путь начинающийся с точки.
571 begin
572   Result := false;   //По умолчанию считаем что путь глобален.
573
574   if Length(PathStr) >= 1 then begin
575     if PathStr[1] = '.' then begin
576       Result := true;
577     end;
578   end;
579 end;
580
581 Function IsGlobalPath(const PathStr : AnsiString) : boolean;
582 //Возвращает true если путь глобальный. Под глобальным понимается путь,
583 //начинающийся на имя диска (функция для вендов).
584 begin
585   Result := false;   //По умолчанию считаем что путь не глобален.
586
587   if Length(PathStr) >= 3 then begin
588     if PathStr[2]+PathStr[3] = ':\' then begin
589       Result := true;
590     end;
591   end;
592 end;
593
594 Function FixLocalPath(const PathStr : AnsiString) : AnsiString;
595 //Правильно оформляет предположительно локальный но неправильно оформленный
596 //путь - чтоб была точка в начале.
597 begin
598   Result := PathStr;
599
600   if IsLocalPath(Result) = false then begin
601     if Length(Result) >= 1 then begin
602       if Result[1] <> '\' then begin
603         Result := '\'+Result;
604       end;
605     end
606     else begin
607       Result := '\'+Result;
608     end;
609     Result := '.'+Result;
610   end;
611 end;
612
613 Function ResolveLocalPath(const LocalPath, GlobalPath : AnsiString) : AnsiString;
614 //Превратить относительный путь в абсолютный. Если относительный путь задан неправильно - предварительно
615 //будет пофиксан. Если относительный путь на самом деле абсолютный - вернет абсолютный путь
616 //который якобы относительный.
617 var
618   TempLocalPath, LocalParsed : AnsiString;
619 begin
620   //Принцип действия: в относительном пути могут быть точки и "двоеточия".
621   //Копируем в результат абсолютный путь, убираем последний слеш,
622   //и дальше парсим локальный путь (по слешам) - встречаем точку - ничО не делаем,
623   //встречаем двоеточие - отрезаем от результата кусок до последнего слеша включительно,
624   //встречаем что-то еще - добавляем в результат слеш и копируем пропарсенное туда же.
625   //В итоге получаем абсолютный путь.
626
627   //Копируем в результат абсолютный путь, убираем последний слеш.
628   Result := ChompPathDelim(GlobalPath);
629   TempLocalPath := LocalPath;     //Это чтоб не портить присланный в функцию аргумент.
630  
631   //Проверяем - надо ли пофиксать локальный путь? А если надо - то как раз пофиксать.
632   if IsGlobalPath(TempLocalPath) = false then begin
633     //Путь не глобален. Оформлен ли он как локальный?
634     if IsLocalPath(TempLocalPath) = false then begin
635       //Как локальный тоже не оформлен - оформляем.
636       TempLocalPath := FixLocalPath(TempLocalPath);
637     end;
638   end;
639  
640   //Теперь - если локальный путь действительно локальный - то работаем.
641   if IsLocalPath(TempLocalPath) = true then begin
642     repeat
643       //Парсим...
644       LocalParsed := Parse(TempLocalPath,'\');
645
646       //Смотря чего выпарсили...
647       if (LocalParsed = '.') or (LocalParsed = '') then begin
648         //Если точка либо просто слеш без всего - то ничего не делать.
649       end
650       else if LocalParsed = '..' then begin
651         //Две точки - поднятся на уровень выше...
652         ParseTail(Result,'\');
653       end
654       else begin
655         //Если там еще какая-то фигня - то добавляем её к пути...
656         Result := Result+'\'+LocalParsed;
657       end;
658
659     until TempLocalPath = '';
660
661     //Готово. Теперь если в LocalPath в конце был слеш - добавим и к результату...
662     if HasLastSlash(LocalPath) = true then begin
663       Result := Result+'\';
664     end;
665   end
666   else begin
667     //Локальный путь на самом деле абсолютный - его и возвращаем.
668     Result := TempLocalPath;
669   end;
670
671 end;
672
673 Function GetLocalPath(const GlobalRoot, FullPath : AnsiString): AnsiString;
674 //Вернуть относительный путь.
675 var
676   TmpGlobalRoot, TmpFullPath, CommonPart, TempStr : AnsiString;
677   Finded : boolean;
678
679 begin
680   //Если глобальный корневой путь не входит в состав полного пути.
681   if StrPos(PChar(UpperCase(FullPath)),PChar(DelLastSlash(UpperCase(GlobalRoot)))) = nil then begin
682
683     //Проверить - совпадает ли первый элемент пути до первого слеша - под вендами это будет буква диска,
684     //под каким нить линухом в теории одинаковая пустая строка.
685     TmpGlobalRoot := AddLastSlash(GlobalRoot);
686     TmpFullPath := AddLastSlash(FullPath);
687     if UpperCase(Parse(TmpGlobalRoot,'\')) = UpperCase(Parse(TmpFullPath,'\')) then begin
688       //Если "диск" один и тот же - тогда формируем относительный путь с "двоеточиями".
689
690       //Ищем общую часть пути...
691       CommonPart := '';
692       Finded := false;
693       TmpGlobalRoot := DelLastSlash(GlobalRoot);
694       TmpFullPath := DelLastSlash(FullPath);
695       repeat
696         TempStr := Parse(TmpGlobalRoot,'\');
697         if UpperCase(TempStr) = UpperCase(Parse(TmpFullPath,'\')) then begin
698           //Кусок пути совпадает.
699           CommonPart := CommonPart+TempStr+'\';
700         end
701         else begin
702           //Не совпало. Делаем отметку. Путь остается с последним слешем.
703           Finded := true;
704         end;
705       until Finded = true;
706
707       //Терь генерим обрезки путей чтоб были без общей части...
708       TmpGlobalRoot := AddLastSlash(GlobalRoot);
709       TmpFullPath := AddLastSlash(FullPath);
710       TmpGlobalRoot := DelLastSlash(RightStr(TmpGlobalRoot,Length(TmpGlobalRoot)-Length(CommonPart)));
711       TmpFullPath := DelLastSlash(RightStr(TmpFullPath,Length(TmpFullPath)-Length(CommonPart)));
712       //Теперь можно сгенерить начало результата из двоеточий...
713       Result := '';
714       repeat
715         //Парсим пока TmpGlobalRoot не опустеет.
716         if Parse(TmpGlobalRoot,'\') <> '' then begin
717           //Если еще чего-то пропарсилось - добавляем две точки...
718           Result := Result+'..\';
719         end;
720       until TmpGlobalRoot = '';
721       //Теперь добавляем обрезок полного пути.
722       Result := Result+TmpFullPath;
723       //Все, можно выходить.
724       Exit;
725     end
726     else begin
727       //Если "диск"-и разные - просто возвращаем полный путь
728       Result := FullPath;
729       Exit;
730     end;
731
732   end;
733
734   //Если у нас один и тот же путь и там и там - то сразу возвращаем ./
735   if UpperCase(DelLastSlash(GlobalRoot)) = UpperCase(DelLastSlash(FullPath)) then begin
736     Result := '.\';
737     Exit;
738   end;
739
740   //Взять копию входной инфы...
741   TmpGlobalRoot := AddLastSlash(GlobalRoot);
742   TmpFullPath := FullPath;
743
744   TmpFullPath := RightStr(TmpFullPath,Length(TmpFullPath)-Length(TmpGlobalRoot));
745   Result := '.\'+TmpFullPath;
746 end;
747
748 Function GetFileSize(const FileName : AnsiString) : Int64;
749 //Получить размер файла.
750 var
751   TheFileStream : TFileStream;
752 begin
753   //По умолчанию возвращаем ноль.
754   Result := 0;
755   //Если файл существует...
756   if FileExists(FileName) = true then begin
757     //... то открываем его...
758     TheFileStream := TFileStream.Create(FileName,fmOpenRead);
759     //... и получаем размер.
760     Result := TheFileStream.Size;
761     //И закрываем файлег.
762     TheFileStream.Free;
763   end;
764 end;
765
766 Function GetFilesNumber(const DirName : AnsiString; const Recursive : boolean) : LongInt;
767 //Подсчитать количество файлов и папок в папке...
768 var
769   SearchRec : TSearchRec;
770 begin
771   Result := 0;
772
773   if FindFirst(AddLastSlash(DirName)+'*', faAnyFile and faDirectory, SearchRec) = 0 then begin
774     //Если поиск начат успешно - начинаем (рекурсивно если надо) считать содержимое.
775     repeat
776       if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then begin
777         //Чего-то нашли.
778         Result := Result+1;
779         //Writeln(SearchRec.Name);
780         if Recursive = true then begin
781           //Если надо действовать рекурсивно...
782           if (SearchRec.Attr and faDirectory) = faDirectory then begin
783             //... и если это директория - то подсчитываем эту директорию.
784             Result := Result+GetFilesNumber(AddLastSlash(DirName)+SearchRec.Name, Recursive);
785           end;
786         end;
787       end;;
788     until FindNext(SearchRec) <> 0;
789     SysUtils.FindClose(SearchRec);
790   end;
791   //Writeln(IntToStr(Result));
792
793 end;
794
795 Function IsDirsChild(const DirName, ChildName : AnsiString) : boolean;
796 //Проверить - принадлежит ли путь ChildName пути DirName.
797 var
798   TempStr : AnsiString;
799 begin
800   //Инициализация.
801   Result := false;     //Изначально путь не считается child-ом
802
803   //Получение "локального пути".
804   TempStr := GetLocalPath(DirName,ChildName);
805   //Проверка - есть ли там ".\"
806   if Length(TempStr) > 1 then begin
807     //Только если искомое могло поместиться в строку.
808     if TempStr[1]+TempStr[2] = '.\' then begin
809       //Изучаемый путь является child-ом DirName
810       Result := true;
811     end;
812   end;
813 end;
814
815 // добаляет расширение если его еще нет.
816 function AddExtensionIfAbsent(const path, extension :String) :String;
817 var
818   ex :AnsiString;
819 begin
820   Result := path;
821   ex := ExtractFileExt(path);
822   if (ex = '') then begin
823     Result := path + extension;
824   end;
825 end;
826
827 // чистое имя файла, без расширения
828 function GetFileNameWithoutExtension(const path :String) :String;
829 var
830   fileName :String;
831   i        :Integer;
832 begin
833   fileName := ExtractFileName(path);
834   i := Length(FileName);
835   while (i > 0) and (FileName[i] <> '.') do Dec(i);
836
837   if (i > 0) then
838     Result := Copy(FileName, 1, i - 1)
839   else
840     Result := fileName;
841 end;
842
843 Function HasLastSlash(const Str : string) : boolean;
844 //Функа для проверки наличия последнего слэша.
845 var
846   I : integer;
847   LastCh : char;
848 begin
849   if Length(Str) > 0 then begin
850     //Смысл что-то проверять есть только если строка не пуста.
851     I := 0;
852     LastCh := #0;
853     repeat
854       I := I+1;
855       If str[I] <> #0 then begin
856         LastCh := str[I];
857       end;
858     until str[I] = #0;
859     //Если последний символ слэш то резулт тру, иначе фалс:
860     If LastCh = '\' then result := true
861     else result := false;
862   end
863   else begin
864     //А если строка пуста - то оно всегда фалсе.
865     Result := false;
866   end;
867 end;
868
869 Function AddLastSlash(const str : string): string;
870 //Функа для добавления в путь последнего слэша (если надо).
871 begin
872   //Если последний символ не слэш - то приплюсоватить его туды.
873   If HasLastSlash(str) = false then result := str+'\'
874   else result := str;
875 end;
876
877 Function DelLastSlash(const Str : string) : string;
878 //Удаляет последний слеш если он есть.
879 begin
880   Result := '';
881   if HasLastSlash(Str) = true then begin
882     SetLength(Result,Length(Str)-1);
883     CopyMemory(@Result[1],@Str[1],Length(Str)-1);
884   end
885   else begin
886     Result := Str;
887   end;
888 end;
889
890 end.
891
Note: See TracBrowser for help on using the browser.