1{%MainUnit lazfileutils.pas}
2
3function FilenameIsAbsolute(const TheFilename: string):boolean;
4begin
5  Result:=FilenameIsUnixAbsolute(TheFilename);
6end;
7
8function FileOpenUTF8(const FileName: string; Mode: Integer): THandle;
9begin
10  Result := SysUtils.FileOpen(UTF8ToSys(FileName), Mode);
11end;
12
13function FileCreateUTF8(const FileName: string): THandle;
14begin
15  Result := SysUtils.FileCreate(UTF8ToSys(FileName));
16end;
17
18function FileCreateUTF8(const FileName: string; Rights: Cardinal): THandle;
19begin
20  Result := SysUtils.FileCreate(UTF8ToSys(FileName), Rights);
21end;
22
23function FileCreateUtf8(const FileName: String; ShareMode: Integer;
24  Rights: Cardinal): THandle;
25begin
26  Result := SysUtils.FileCreate(UTF8ToSys(FileName), ShareMode, Rights);
27end;
28
29function FileGetAttrUTF8(const FileName: String): Longint;
30begin
31  Result:=SysUtils.FileGetAttr(UTF8ToSys(Filename));
32end;
33
34function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;
35begin
36  Result:=SysUtils.FileSetAttr(UTF8ToSys(Filename),Attr);
37  InvalidateFileStateCache(Filename);
38end;
39
40function FileExistsUTF8(const Filename: string): boolean;
41begin
42  Result:=SysUtils.FileExists(UTF8ToSys(Filename));
43end;
44
45function DirectoryExistsUTF8(const Directory: string): Boolean;
46begin
47  Result:=SysUtils.DirectoryExists(UTF8ToSys(Directory));
48end;
49
50function FileAgeUTF8(const FileName: string): Longint;
51begin
52  Result:=SysUtils.FileAge(UTF8ToSys(Filename));
53end;
54
55function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
56begin
57  Result := SysUtils.FileSetDate(UTF8ToSys(Filename), Age);
58  InvalidateFileStateCache(Filename);
59end;
60
61function FileSizeUtf8(const Filename: string): int64;
62var
63  st: baseunix.stat;
64  SysFileName: String;
65begin
66  SysFileName := Utf8ToSys(FileName);
67  if not fpstat(pointer(SysFileName),st{%H-})>=0 then
68    exit(-1);
69  Result := st.st_size;
70end;
71
72{------------------------------------------------------------------------------
73  function ReadAllLinks(const Filename: string;
74    ExceptionOnError: boolean): string;
75 ------------------------------------------------------------------------------}
76function ReadAllLinks(const Filename: string;
77  ExceptionOnError: boolean): string;
78var
79  LinkFilename: string;
80  AText: string;
81  Depth: Integer;
82begin
83  Result:=Filename;
84  Depth:=0;
85  while Depth<12 do begin
86    inc(Depth);
87    LinkFilename:=FpReadLink(Result);
88    if LinkFilename='' then begin
89      AText:='"'+Filename+'"';
90      case fpGetErrno() of
91      ESysEAcces:
92        AText:='read access denied for '+AText;
93      ESysENoEnt:
94        AText:='a directory component in '+AText
95                            +' does not exist or is a dangling symlink';
96      ESysENotDir:
97        AText:='a directory component in '+AText+' is not a directory';
98      ESysENoMem:
99        AText:='insufficient memory';
100      ESysELoop:
101        AText:=AText+' has a circular symbolic link';
102      else
103        // not a symbolic link, just a regular file
104        exit;
105      end;
106      if (not ExceptionOnError) then begin
107        Result:='';
108        exit;
109      end;
110      raise EFOpenError.Create(AText);
111    end else begin
112      if not FilenameIsAbsolute(LinkFilename) then
113        Result:=ResolveDots(ExtractFilePath(Result)+LinkFilename)
114      else
115        Result:=LinkFilename;
116    end;
117  end;
118  // probably an endless loop
119  if ExceptionOnError then
120    raise EFOpenError.Create('too many links, maybe an endless loop.')
121  else
122    Result:='';
123end;
124
125function GetPhysicalFilename(const Filename: string;
126  OnError: TPhysicalFilenameOnError): string;
127begin
128  Result:=GetUnixPhysicalFilename(Filename,OnError=pfeException);
129  if (Result='') and (OnError=pfeOriginal) then
130    Result:=Filename;
131end;
132
133function GetUnixPhysicalFilename(const Filename: string;
134  ExceptionOnError: boolean): string;
135var
136  OldPath: String;
137  NewPath: String;
138  p: PChar;
139begin
140  Result:=Filename;
141  p:=PChar(Result);
142  repeat
143    while p^='/' do
144      inc(p);
145    if p^=#0 then exit;
146    if p^<>'/' then
147    begin
148      repeat
149        inc(p);
150      until p^ in [#0,'/'];
151      OldPath:=LeftStr(Result,p-PChar(Result));
152      NewPath:=ReadAllLinks(OldPath,ExceptionOnError);
153      if NewPath='' then exit('');
154      if OldPath<>NewPath then
155      begin
156        Result:=NewPath+copy(Result,length(OldPath)+1,length(Result));
157        p:=PChar(Result)+length(NewPath);
158      end;
159    end;
160  until false;
161  Result:=ResolveDots(Result);
162end;
163
164function CreateDirUTF8(const NewDir: String): Boolean;
165begin
166  Result:=SysUtils.CreateDir(UTF8ToSys(NewDir));
167end;
168
169function RemoveDirUTF8(const Dir: String): Boolean;
170begin
171  Result:=SysUtils.RemoveDir(UTF8ToSys(Dir));
172end;
173
174function DeleteFileUTF8(const FileName: String): Boolean;
175begin
176  Result:=SysUtils.DeleteFile(UTF8ToSys(Filename));
177  if Result then
178    InvalidateFileStateCache;
179end;
180
181function RenameFileUTF8(const OldName, NewName: String): Boolean;
182begin
183  Result:=SysUtils.RenameFile(UTF8ToSys(OldName),UTF8ToSys(NewName));
184  if Result then
185    InvalidateFileStateCache;
186end;
187
188function SetCurrentDirUTF8(const NewDir: String): Boolean;
189begin
190  Result:=SysUtils.SetCurrentDir(UTF8ToSys(NewDir));
191end;
192
193function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec
194  ): Longint;
195begin
196  Result:=SysUtils.FindFirst(UTF8ToSys(Path),Attr,Rslt);
197  Rslt.Name:=SysToUTF8(Rslt.Name);
198end;
199
200function FindNextUTF8(var Rslt: TSearchRec): Longint;
201begin
202  Rslt.Name:=UTF8ToSys(Rslt.Name);
203  Result:=SysUtils.FindNext(Rslt);
204  Rslt.Name:=SysToUTF8(Rslt.Name);
205end;
206
207
208function ExpandFileNameUTF8(const FileName: string; BaseDir: string): string;
209var
210  IsAbs: Boolean;
211  CurDir, HomeDir, Fn: String;
212begin
213  Fn := FileName;
214  ForcePathDelims(Fn);
215  IsAbs := FileNameIsUnixAbsolute(Fn);
216  if (not IsAbs) then
217  begin
218    CurDir := GetCurrentDirUtf8;
219    if ((Length(Fn) > 1) and (Fn[1] = '~') and (Fn[2] = '/')) or (Fn = '~') then
220    begin
221      HomeDir := GetEnvironmentVariableUTF8('HOME');
222      if not FileNameIsUnixAbsolute(HomeDir) then
223        HomeDir := ExpandFileNameUtf8(HomeDir,'');
224      Fn := HomeDir + Copy(Fn,2,length(Fn));
225      IsAbs := True;
226    end;
227  end;
228  if IsAbs then
229  begin
230    Result := ResolveDots(Fn);
231  end
232  else
233  begin
234    if (BaseDir = '') then
235      Fn := IncludeTrailingPathDelimiter(CurDir) + Fn
236    else
237      Fn := IncludeTrailingPathDelimiter(BaseDir) + Fn;
238    Fn := ResolveDots(Fn);
239    //if BaseDir is not absolute then this needs to be expanded as well
240    if not FileNameIsUnixAbsolute(Fn) then
241      Fn := ExpandFileNameUtf8(Fn, '');
242    Result := Fn;
243  end;
244end;
245
246function GetCurrentDirUTF8: String;
247begin
248  Result:=SysToUTF8(SysUtils.GetCurrentDir);
249end;
250
251function FileIsExecutable(const AFilename: string): boolean;
252var
253  Info : Stat;
254begin
255  // first check AFilename is not a directory and then check if executable
256  Result:= (FpStat(AFilename,info{%H-})<>-1) and FPS_ISREG(info.st_mode) and
257           (BaseUnix.FpAccess(AFilename,BaseUnix.X_OK)=0);
258end;
259
260procedure CheckIfFileIsExecutable(const AFilename: string);
261var
262  AText: String;
263begin
264  // TProcess does not report, if a program can not be executed
265  // to get good error messages consider the OS
266  if not FileExistsUTF8(AFilename) then begin
267    raise Exception.Create(Format(lrsFileDoesNotExist, [AFilename]));
268  end;
269  if DirPathExists(AFilename) then begin
270    raise Exception.Create(Format(lrsFileIsADirectoryAndNotAnExecutable, [
271      AFilename]));
272  end;
273  if BaseUnix.FpAccess(AFilename,BaseUnix.X_OK)<>0 then
274  begin
275    AText:='"'+AFilename+'"';
276    case fpGetErrno() of
277    ESysEAcces:
278      AText:=Format(lrsReadAccessDeniedFor, [AText]);
279    ESysENoEnt:
280      AText:=Format(lrsADirectoryComponentInDoesNotExistOrIsADanglingSyml, [
281        AText]);
282    ESysENotDir:
283      AText:=Format(lrsADirectoryComponentInIsNotADirectory, [Atext]);
284    ESysENoMem:
285      AText:=lrsInsufficientMemory;
286    ESysELoop:
287      AText:=Format(lrsHasACircularSymbolicLink, [AText]);
288    else
289      AText:=Format(lrsIsNotExecutable, [AText]);
290    end;
291    raise Exception.Create(AText);
292  end;
293  // ToDo: xxxbsd
294end;
295
296function FileIsSymlink(const AFilename: string): boolean;
297begin
298  Result := FpReadLink(AFilename) <> '';
299end;
300
301procedure CheckIfFileIsSymlink(const AFilename: string);
302var
303  AText: string;
304begin
305  // to get good error messages consider the OS
306  if not FileExistsUTF8(AFilename) then begin
307    raise Exception.Create(Format(lrsFileDoesNotExist, [AFilename]));
308  end;
309  if FpReadLink(AFilename)='' then begin
310    AText:='"'+AFilename+'"';
311    case fpGetErrno() of
312    ESysEAcces:
313      AText:=Format(lrsReadAccessDeniedFor, [AText]);
314    ESysENoEnt:
315      AText:=Format(lrsADirectoryComponentInDoesNotExistOrIsADanglingSyml2, [
316        AText]);
317    ESysENotDir:
318      AText:=Format(lrsADirectoryComponentInIsNotADirectory2, [Atext]);
319    ESysENoMem:
320      AText:=lrsInsufficientMemory;
321    ESysELoop:
322      AText:=Format(lrsHasACircularSymbolicLink, [AText]);
323    else
324      AText:=Format(lrsIsNotASymbolicLink, [AText]);
325    end;
326    raise Exception.Create(AText);
327  end;
328end;
329
330function FileIsHardLink(const AFilename: string): boolean;
331var
332  H: THandle;
333  FileInfo: stat;
334begin
335  Result := false;
336  H := FileOpenUtf8(aFilename, fmOpenRead);
337  if H <> feInvalidHandle then
338  begin
339    if FPFStat(H, FileInfo{%H-}) = 0 then
340      Result := (FileInfo.st_nlink > 1);
341    FileClose(H);
342  end;
343end;
344
345function FileIsReadable(const AFilename: string): boolean;
346begin
347  Result := BaseUnix.FpAccess(AFilename, BaseUnix.R_OK) = 0;
348end;
349
350function FileIsWritable(const AFilename: string): boolean;
351begin
352  Result := BaseUnix.FpAccess(AFilename, BaseUnix.W_OK) = 0;
353end;
354
355
356function IsUNCPath(const Path: String): Boolean;
357begin
358  Result := false;
359end;
360
361function ExtractUNCVolume(const Path: String): String;
362begin
363  Result := '';
364end;
365
366function GetFileDescription(const AFilename: string): string;
367var
368  info: Stat;
369  // permissions
370  // user
371  // group
372  // size
373  // date
374  // time
375  mode: mode_t;
376begin
377  Result:='';
378  if not (FpStat(AFilename,info{%H-})=0) then exit;
379
380  // permissions
381  // file type
382  mode:= info.st_mode;
383  if STAT_IFLNK and mode=STAT_IFLNK then
384    Result:=Result+'l'
385  else
386  if STAT_IFDIR and mode=STAT_IFDIR then
387    Result:=Result+'d'
388  else
389  if STAT_IFBLK and mode=STAT_IFBLK then
390    Result:=Result+'b'
391  else
392  if STAT_IFCHR and mode=STAT_IFCHR then
393    Result:=Result+'c'
394  else
395    Result:=Result+'-';
396  // user permissions
397  if STAT_IRUSR and mode=STAT_IRUsr then
398    Result:=Result+'r'
399  else
400    Result:=Result+'-';
401  if STAT_IWUsr and mode=STAT_IWUsr then
402    Result:=Result+'w'
403  else
404    Result:=Result+'-';
405  if STAT_IXUsr and mode=STAT_IXUsr then
406    Result:=Result+'x'
407  else
408    Result:=Result+'-';
409  // group permissions
410  if STAT_IRGRP and mode=STAT_IRGRP then
411    Result:=Result+'r'
412  else
413    Result:=Result+'-';
414  if STAT_IWGRP and mode=STAT_IWGRP then
415    Result:=Result+'w'
416  else
417    Result:=Result+'-';
418  if STAT_IXGRP and mode=STAT_IXGRP then
419    Result:=Result+'x'
420  else
421    Result:=Result+'-';
422  // other permissions
423  if STAT_IROTH and mode=STAT_IROTH then
424    Result:=Result+'r'
425  else
426    Result:=Result+'-';
427  if STAT_IWOTH and mode=STAT_IWOTH then
428    Result:=Result+'w'
429  else
430    Result:=Result+'-';
431  if STAT_IXOTH and mode=STAT_IXOTH then
432    Result:=Result+'x'
433  else
434    Result:=Result+'-';
435
436
437  // user name
438  //Result:=Result+' Owner: '+IntToStr(info.uid)+'.'+IntToStr(info.gid);
439
440  // size
441  Result:=Result+lrsSize+IntToStr(info.st_size);
442
443  // date + time
444  Result:=Result+lrsModified;
445  try
446    Result:=Result+FormatDateTime('DD/MM/YYYY hh:mm',
447                           FileDateToDateTime(FileAgeUTF8(AFilename)));
448  except
449    Result:=Result+'?';
450  end;
451end;
452
453
454function GetAppConfigDirUTF8(Global: Boolean; Create: boolean = false): string;
455begin
456  Result := SysToUTF8(SysUtils.GetAppConfigDir(Global));
457  if Result = '' then exit;
458  if Create and not ForceDirectoriesUTF8(Result) then
459    raise EInOutError.Create(Format(lrsUnableToCreateConfigDirectoryS,[Result]));
460end;
461
462function GetAppConfigFileUTF8(Global: Boolean; SubDir: boolean;
463  CreateDir: boolean): string;
464var
465  Dir: string;
466begin
467  Result := SysToUTF8(SysUtils.GetAppConfigFile(Global,SubDir));
468  if not CreateDir then exit;
469  Dir := ExtractFilePath(Result);
470  if Dir = '' then exit;
471  if not ForceDirectoriesUTF8(Dir) then
472    raise EInOutError.Create(Format(lrsUnableToCreateConfigDirectoryS,[Dir]));
473end;
474
475function GetShellLinkTarget(const FileName: string): string;
476begin
477  Result := Filename;
478end;
479
480procedure InitLazFileUtils;
481begin
482  //dummy
483end;
484
485procedure FinalizeLazFileUtils;
486begin
487  //dummy
488end;
489