Recent

Author Topic: TrueName  (Read 1280 times)

Riccardo Ferrari

  • New member
  • *
  • Posts: 8
TrueName
« on: February 07, 2024, 10:30:07 pm »
I have the following need, which I'm afraid is not very common, because I've searched online for a solution, but I haven't found anything, not only the solution (it happens that I can't find it), but even someone else who has asked the usual question.
So: Windows is not case sensitive. Luckily, when I use Linux, I go crazy for capital letters, but this has an aesthetic effect that disturbs me (I know it's not a big problem).
If a file is called Abcdefhi.Jkl and I type abcDEFHI.jkL instead, the program is fine anyway, it recognises it and continues processing.
The aesthetics is that it remains "badly" written throughout the program. When the program has to return the filename, it does so as it was set, not as it actually is.
So I wrote a function, which I called TrueName, which converts the set name to the real name, preserving the case. Unfortunately, ExpandFileName doesn't do this as I had hoped; if you have spelt it "badly", it doesn't fix things.
TrueName works like this: instead of checking the existence of the file with FileExists, as I usually do, I search for the set name with FindFirst, then I read the variable <dot>Name of the TSearchRec record, which corresponds to the real name with the correct capitalisation, obviously FindFirst<>0 corresponds to False of FileExists.
Is everything solved? Far from it.
Firstly, there may be a more efficient algorithm than mine, if so I would be delighted. Secondly, the FindFirst trick only applies to the name, the rest of the path, i.e. the names of the various parent directories, still retain the case set by the keyboard, even if they differ in case from the original.
So I should apply the FindFirst trick to each directory in the path. One at a time. So if my algorithm was not efficient, the inefficiency would be multiplied by the depth of the path.
Maybe, I wondered, there is a function already written that returns the true full name of the path.
Do you know of any? Thanks for your attention.
Sorry for the length, but it doesn't seem like a very serious problem to me, so I had to explain it well to avoid misunderstandings.

Bart

  • Hero Member
  • *****
  • Posts: 5496
    • Bart en Mariska's Webstek
Re: TrueName
« Reply #1 on: February 07, 2024, 10:38:17 pm »
Something like this used to be in LCL sources:

Code: Pascal  [Select][+][-]
  1. function FindDiskFilename(const Filename: string): string;
  2.    // Searches for the filename case on disk.
  3.    // if it does not exist, only the found path will be improved
  4.    // For example:
  5.    //   If Filename='file' and there is only a 'File' then 'File' will be returned.
  6. var
  7.  StartPos: Integer;
  8.  EndPos: LongInt;
  9.  FileInfo: TSearchRec;
  10.  CurDir: String;
  11.  CurFile: String;
  12.  AliasFile: String;
  13.  Ambiguous: Boolean;
  14.  FileNotFound: Boolean;
  15. begin
  16.  Result:=Filename;
  17.  // check every directory and filename
  18.  StartPos:=1;
  19.  {$IFDEF Windows}
  20.  // uppercase Drive letter and skip it
  21.  if ((length(Result)>=2) and (Result[1] in ['A'..'Z','a'..'z'])
  22.  and (Result[2]=':')) then begin
  23.    StartPos:=3;
  24.    if Result[1] in ['a'..'z'] then
  25.      Result[1] := UpCase(Result[1]);
  26.  end;
  27.  {$ENDIF}
  28.  FileNotFound:=false;
  29.  repeat
  30.    // skip PathDelim
  31.    while (StartPos<=length(Result)) and (Result[StartPos]=PathDelim) do
  32.      inc(StartPos);
  33.    // find end of filename part
  34.    EndPos:=StartPos;
  35.    while (EndPos<=length(Result)) and (Result[EndPos]<>PathDelim) do
  36.      inc(EndPos);
  37.    if EndPos>StartPos then begin
  38.      // search file
  39.      CurDir:=copy(Result,1,StartPos-1);
  40.      CurFile:=copy(Result,StartPos,EndPos-StartPos);
  41.      AliasFile:='';
  42.      Ambiguous:=false;
  43.      if FindFirstUTF8(CurDir+AllFilesMask,faAnyFile,FileInfo)=0 then
  44.      begin
  45.        repeat
  46.          // check if special file
  47.          if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='')
  48.          then
  49.            continue;
  50.          if CompareFilenamesIgnoreCase(FileInfo.Name,CurFile)=0 then begin
  51.            //writeln('FindDiskFilename ',FileInfo.Name,' ',CurFile);
  52.            if FileInfo.Name=CurFile then begin
  53.              // file found, has already the correct name
  54.              AliasFile:='';
  55.              break;
  56.            end else begin
  57.              // alias found, but has not the correct name
  58.              if AliasFile='' then begin
  59.                AliasFile:=FileInfo.Name;
  60.              end else begin
  61.                // there are more than one candidate
  62.                Ambiguous:=true;
  63.              end;
  64.            end;
  65.          end;
  66.        until FindNextUTF8(FileInfo)<>0;
  67.      end else
  68.        FileNotFound:=true;
  69.      FindCloseUTF8(FileInfo);
  70.      if FileNotFound then break;
  71.      if (AliasFile<>'') and (not Ambiguous) then begin
  72.        // better filename found -> replace
  73.        Result:=CurDir+AliasFile+copy(Result,EndPos,length(Result));
  74.      end;
  75.    end;
  76.    StartPos:=EndPos+1;
  77.  until StartPos>length(Result);
  78. end;

Bart

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11984
  • FPC developer.
Re: TrueName
« Reply #2 on: February 07, 2024, 10:43:05 pm »
I assume the easiest would be to make expandfilename do what you want.

The expandfilename code is very flexible with ifdefs, including for case insensitive and -preserving file systems supporting all FPC's target OSes. You could try to include that source with appropriate ifdefs for your truename().

But except for some special cases it is fighting against the tide. If your OS is case sensitive it is case sensitive. Deal with it, it will save you a lot of grief long term.
« Last Edit: February 07, 2024, 10:55:31 pm by marcov »

Curt Carpenter

  • Hero Member
  • *****
  • Posts: 574
Re: TrueName
« Reply #3 on: February 07, 2024, 10:50:31 pm »
Same with your language:  it is what it is. 

Kays

  • Hero Member
  • *****
  • Posts: 614
  • Whasup!?
    • KaiBurghardt.de
Re: TrueName
« Reply #4 on: February 08, 2024, 10:12:06 pm »
I have the following need, which I'm afraid is not very common, because I've searched online for a solution, but I haven't found anything, not only the solution (it happens that I can't find it), but even someone else who has asked the usual question. […]
You chose the thread title TrueName. The missing vocable is canonicalize. You will find more results with this keyword (but not all, for instance Pathname conversion).

NB: In the UNIX world canonical pathnames simply means evaluating and removing unnecessary . (same directory) and .. (parent directory) components and resolving symbolic links. I can’t speak for Winblows’ understanding.
Yours Sincerely
Kai Burghardt

Riccardo Ferrari

  • New member
  • *
  • Posts: 8
Re: TrueName
« Reply #5 on: February 10, 2024, 05:56:46 am »
Same with your language:  it is what it is.
Thank you for your attention, but I'm not sure I understood what you wrote.

Riccardo Ferrari

  • New member
  • *
  • Posts: 8
Re: TrueName
« Reply #6 on: February 10, 2024, 06:28:52 am »
[...] for instance Pathname conversion).
Thank you very much. The topic you mentioned contains the solution and a source very similar to the one I came up with in parallel.
I was hoping there was a direct way, but instead you have to go backwards through all the directories.
If you're interested, I'll post the code I wrote following the suggestions I received.
Code: Pascal  [Select][+][-]
  1. {--------------------------------------------------------------------------}
  2. Uses sysutils;
  3. {--------------------------------------------------------------------------}
  4. Var
  5.   FName,TName:String;
  6. {--------------------------------------------------------------------------}
  7. Function TrueName(F:AnsiString):AnsiString;
  8.   var
  9.     u,v:Unicodestring;
  10.     a,z:AnsiString;
  11.     m:TFilenameCaseMatch;
  12.   {----------------------------------------}
  13.   Function DueSlash(Sh:AnsiString):Boolean;
  14.       begin
  15.       // delete(Sh,1,pos(PathDelim,Sh)); e' sempre 3
  16.       delete(Sh,1,3);
  17.       if pos(PathDelim,Sh)>0 then DueSlash:=True else DueSlash:=False;
  18.       end; // Function DueSlash
  19.   {----------------------------------------}
  20.   Function  StripEnd(Se:AnsiString):AnsiString;
  21.       begin
  22.       if AnsiLastChar(Se)=PathDelim then delete(Se,length(Se),1);
  23.       StripEnd:=Se;
  24.       end; // Function StripEnd
  25.   {----------------------------------------}
  26.   begin
  27.       z:='';
  28.       u:=f;
  29.       v:=ExpandFileNameCase(u,m);
  30.       a:=v;
  31.       while DueSlash(a) do
  32.         begin
  33.           if z='' then z:=ExtractFileName(a) else z:=ExtractFileName(a)+PathDelim+z;
  34.           a:=StripEnd(ExtractFilePath(a));
  35.           u:=a;
  36.           v:=ExpandFileNameCase(u,m);
  37.           a:=v;
  38.         end;
  39.         z:=copy(a,1,3)+ExtractFileName(a)+PathDelim+z;
  40.         TrueName:=z;
  41.   end;
  42. {--------------------------------------------------------------------------}
  43. Begin
  44.   FName:=paramstr(1);
  45.   TName:=TrueName(FName);
  46.   WriteLn;
  47.   WriteLn('Input   : ',FName);
  48.   WriteLn('Truename: ',TName);
  49. End.
  50. {--------------------------------------------------------------------------}
  51.  
I realise now that there is a bit of Italian in the code.
I'm not correcting, but I'm posting the translations, assuming they're of interest.
The comment "e' sempre 3" means "it's always 3" (at least in Windows).
The name of the function DueSlash, means TwoSlash, i.e. the function checks if there are two slashes (actually backslashes in Windows).
Since "Due" (number Two (2) in Italian) has a completely different meaning in English, I wanted to clarify it.

Thaddy

  • Hero Member
  • *****
  • Posts: 16343
  • Censorship about opinions does not belong here.
Re: TrueName
« Reply #7 on: February 10, 2024, 07:42:51 pm »
I assume the easiest would be to make expandfilename do what you want.
And realize the the NTFS file system on Windows is actually Case sensitive.
The CreateFile API, which most file creation and opening requests use, mediates the case sensitivity of file access.
The FILE_FLAG_POSIX_SEMANTICS flag passed to CreateFile determines whether file opening or creation is case-sensitive.
In the find files API you can also use the flag FIND_FIRST_EX_CASE_SENSITIVE
Maybe you can do something with that.

See e.g. https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfileexw
« Last Edit: February 10, 2024, 07:59:01 pm by Thaddy »
There is nothing wrong with being blunt. At a minimum it is also honest.

 

TinyPortal © 2005-2018