How can I separate the Extension part of a File Name?

I am trying to add image files into a library, and as part of that, to optionally copy or move & rename them to give them 8.3 file names. How should I go separating the file extension from the name in order to attach it to a fresh file name?

If you have Capesoft’s String Theory it has a simple method for splitting file names:

Otherwise you can use the FnSplit API call:

You can use PathSplit function from CWUtil.INC, or just find last dot position in a filename:
LastDotPos = INSTRING('.', FileName, -1, SIZE(FileName))

1 Like

In addition to the other responses, if you are truly going for an 8.3 filename, you might need to do extra stuff if your extension is longer than 3 characters.

Also, some systems could have 8.3 disabled. Google 8dot3name.

What value of the extension is being returned for the filename X:\Tests.1\nothing ?

1 Like

Do you mean " What value of the last dot position is being returned for the filename X:\Tests.1\nothing ?"?

Actually good point, it needs to keep in mind that there could be files with no extension.

StringTheory handles this. I have seen it in the StrngTheoy source a few days back.

Good Point I should think of when I have a full name to first cutoff after last backslash:

LastBSPos  = INSTRING('\', PathAndFile, -1, SIZE(PathAndFile))  !End of Path at last "\"
FileName   = SUB(PathAndFile, LastBSPos+1, 255)      !Take after x:\path\ FILENAME.EXT
LastDotPos = INSTRING('.', FileName, -1, SIZE(FileName))  
FileExten  = SUB(FileName, LastDotPos+1, 255)

What’s about the filename X:nothing?
What’s about the directory name X:\Level.1\Level.2\Level.3\.. ?

Exten would be blank which I think is correct

I would typically know if I had a Directory Name only instead of a File Name.

Calling the File Dialog with File:Directory can only return a File Name … But often there is also an ENTRY() for a File Name so it is possible a User could paste into that a Path like C:\MyDocs\Archive.PDF\June2023 that would confuse that Extension code.

Unfortunately the Clarion Exists() will not indicate File or Path, I have required that a few times. Code like this can be used:

    ExistsFileOrPath(STRING FileOrPathName,<*LONG OutAttributes>),BYTE  !Return 1=File 2=Path 0=N/A
      GetFileAttributes(*CSTRING FileName),LONG,PASCAL,RAW,DLL(1),NAME('GetFileAttributesA')

ExistsFileOrPath PROCEDURE(STRING FN, <*LONG OutAttr>)!,BYTE  !1=File 2=Path
cName   CSTRING(261),AUTO
ForP12  BYTE
!_INVALID_FILE_ATTRIBUTES  EQUATE(-1)  !If function fails it returns INVALID_FILE_ATTRIBUTES. Call GetLastError() for error information, 
!_FILE_ATTRIBUTE_DIRECTORY EQUATE(10h) !The handle that identifies a directory
  Attr = GetFileAttributes(cName)
  IF ~OMITTED(OutAttr) THEN OutAttr=Attr.
  IF Attr <> -1 THEN ForP12=CHOOSE(~BAND(Attr,10h),1,2).

I see that this function PathSplit is a rename of fnsplit. I used the fnsplit from clib.clw in some of my classes. The clib.clw is from 1995 though, so I wonder if it is present in newer Clarion releases?

fnsplit has been there forever, and still remains.

The goofy thing is the FN Split in CwUtil is just the C version declared in the CwUtil.INC file with a better name PathSplit than NAME('_fnsplit').

  !-- CwUtil.INC in LibSrc ---
    PathSplit(CONST *CSTRING path, <*CSTRING drive>, <*CSTRING dir>, <*CSTRING file>, <*CSTRING ext>), SIGNED, PROC, RAW, NAME('_fnsplit')
    PathMerge(*CSTRING path, <*CSTRING drive>, <*CSTRING dir>, <*CSTRING file>, <*CSTRING ext>), SIGNED, PROC, RAW, NAME('_fnmerge')

So you have to define all CSTRING variables to call it. IMO the purpose of CwUtil is to make wrapper functions so its easy to call API/RTL functions with normal Clarion types like STRING. Its not like it was hard to do :frowning:

Not disagreeing. I always just used my own wrapper func.