Pathname translation

From BeebWiki
Jump to: navigation, search

Usually a program does not, and should not, need to know what type of filenames the underlying filing system is using. The program should accept whatever filenames the user supplies, and pass them onto the filing system. However, there are some cases where the actual filename format is needed and the program needs to be able to translate filenames.

Examples would include:

  • a zipfile handler, filenames within zipfiles are defined to be Unix filenames, with "/" as directory separators and "." as extension separators.
  • a program generating or receiving URLs and needing to map them to/from the local filesystem. URLs are defined to be unix filenames, with "/" as directory separators and "." as extension separators.
  • a server application serving files on one platform where the client is another platform, for instance a server running on DOS/Windows serving files to a BBC client.
  • extracting or manipulating "foreign" disk images where the program wants to display file information in the format the foreign system would display it.

The Filename library provides platform-independant functions for converting filenames between BBC, DOS/Windows and Unix/Zip/URL formats.

If you know that you will only be dealing with leafnames or with simply full-canonicalised pathnames, then the simpler Filename translation code can be used.

FNfn_bbctodos(file$), FNfn_bbctounix(file$), FNfn_frombbc(file$), FNfn_tobbc(file$)

These functions convert BBC filenames to other formats. FNfn_frombbc() converts a BBC filename to the format of the local platform, FNfn_tobbc() converts a local filename to a BBC filename; both according to the setting of os%.

Examples include:

 FNfn_bbctodos("$.docs.letters/cv")  ->  \docs\
 FNfn_bbctodos("^.info##")           ->  ..\info??
 FNfn_bbctodos(":A.text.why?")       ->  A:\text\why#
 FNfn_bbctounix("$.docs.letters/cv") ->  /docs/
 FNfn_bbctounix("^.info##")          ->  ../info??
 FNfn_bbctounix(":A.text.why?")      ->  A:/text/why#

If running on BBC/RISC OS:

 FNfn_frombbc("$.docs.letters/cv")   ->  $.docs.letters/cv
 FNfn_frombbc("^.info##")            ->  ^.info##
 FNfn_frombbc(":A.text.why?")        ->  :A.text.why?
 FNfn_tobbc("$.docs.letters/cv")     ->  $.docs.letters/cv
 FNfn_tobbc("^.info##")              ->  ^.info##
 FNfn_tobbc(":A.text.why?")          ->  :A.text.why?

If running on DOS/Windows:

 FNfn_frombbc("$.docs.letters/cv")   ->  \docs\
 FNfn_frombbc("^.info##")            ->  ..\info??
 FNfn_frombbc(":A.text.why?")        ->  A:\text\why#
 FNfn_tobbc("\docs\")      ->  $.docs.letters/cv
 FNfn_tobbc("..\info??")             ->  ^.info##
 FNfn_tobbc("A:\text\why#")          ->  :A.text.why?

If running on UNIX:

 FNfn_frombbc("$.docs.letters/cv")   ->  /docs/
 FNfn_frombbc("^.info##")            ->  ../info??
 FNfn_frombbc(":A.text.why?")        ->  A:/text/why#
 FNfn_tobbc("/docs/")      ->  $.docs.letters/cv
 FNfn_tobbc("../info??")             ->  ^.info##
 FNfn_tobbc("A:/text/why#")          ->  :A.text.why?

FNfn_dostobbc(file$), FNfn_dostounix(file$), FNfn_fromdos(file$), FNfn_todos(file$)

These functions convert DOS/Windows filenames to other formats. FNfn_fromdos() converts a DOS/Windows filename to the format of the local platform, FNfn_todos() converts a local filename to a DOS/Windows filename; both according to the setting of os%.

Examples include:

 FNfn_dostobbc("\docs\")   ->  $.docs.letters/cv
 FNfn_dostobbc("..\info??")          ->  ^.info##
 FNfn_dostobbc("A:\text\why#")       ->  :A.text.why?
 FNfn_dostounix("\docs\")  ->  /docs/
 FNfn_dostounix("..\info??")         ->  ../info??
 FNfn_dostounix("A:\text\why#")      ->  A:/text/why#

If running on BBC/RISC OS:

 FNfn_fromdos("\docs\")    ->  $.docs.letters/cv
 FNfn_fromdos("..\info??")           ->  ^.info##
 FNfn_fromdos("A:\text\why#")        ->  :A.text.why?
 FNfn_todos("$.docs.letters/cv")     ->  \docs\
 FNfn_todos("^.info##")              ->  ..\info??
 FNfn_todos(":A.text.why?")          ->  A:\text\why#

If running on DOS/Windows:

 FNfn_fromdos("\docs\")    ->  \docs\letters/cv
 FNfn_fromdos("..\info??")           ->  ..\info??
 FNfn_fromdos("A:\text\why#")        ->  A:\text\why?
 FNfn_todos("\docs\")      ->  \docs\
 FNfn_todos("..\info??")             ->  ..\info??
 FNfn_todos("A:\text\why#")          ->  A:\text\why#

If running on UNIX:

 FNfn_fromdos("\docs\")    ->  /docs/
 FNfn_fromdos("..\info??")           ->  ../info??
 FNfn_fromdos("A:\text\why#")        ->  A:/text/why#
 FNfn_todos("/docs/")      ->  \docs\
 FNfn_todos("../info??")             ->  ..\info??
 FNfn_todos("A:/text/why#")          ->  A:\text\why#

FNfn_unixtobbc(file$), FNfn_unixtodos(file$), FNfn_fromunix(file$), FNfn_tounix(file$)

These functions convert UNIX/ZIP/URL filenames to other formats. FNfn_fromunix() converts a UNIX/ZIP/URL filename to the format of the local platform, FNfn_tounix() converts a local filename to a UNIX/ZIP/URL filename; both according to the setting of os%.

Examples include:

 FNfn_unixtobbc("/docs/")  ->  $.docs.letters/cv
 FNfn_unixtobbc("../info??")         ->  ^.info##
 FNfn_unixtobbc("A:/text/why#")      ->  :A.text.why?
 FNfn_unixtodos("\docs\")  ->  \docs\
 FNfn_unixtodos("..\info??")         ->  ..\info??
 FNfn_unixtodos("A:\text\why#")      ->  A:\text\why#

If running on BBC/RISC OS:

 FNfn_fromunix("/docs/")   ->  $.docs.letters/cv
 FNfn_fromunix("../info??")          ->  ^.info##
 FNfn_fromunix("A:/text/why#")       ->  :A.text.why?
 FNfn_tounix("$.docs.letters/cv")    ->  /docs/
 FNfn_tounix("^.info##")             ->  ../info??
 FNfn_tounix(":A.text.why?")         ->  A:/text/why#

If running on DOS/Windows:

 FNfn_fromunix("/docs/")   ->  \docs\letters/cv
 FNfn_fromunix("../info??")          ->  ..\info??
 FNfn_fromunix("A:/text/why#")       ->  A:\text\why?
 FNfn_tounix("\docs\")     ->  /docs/
 FNfn_tounix("..\info??")            ->  ../info??
 FNfn_tounix("A:\text\why#")         ->  A:/text/why#

If running on UNIX:

 FNfn_fromunix("/docs/")   ->  /docs/
 FNfn_fromunix("../info??")          ->  ../info??
 FNfn_fromunix("A:/text/why#")       ->  A:/text/why#
 FNfn_tounix("/docs/")      ->  /docs/
 FNfn_tounix("../info??")             ->  ../info??
 FNfn_tounix("A:/text/why#")          ->  A:/text/why#

Technical information

Different platforms use different characters to indicate different filename components such as directory and extension separators, drive specifiers, current, root and parent directories.

                      BBC        DOS/Windows      UNIX/ZIP/URL
Drive                 :d            d:
Directory separator   .             \                /
Extension separator   /             .                .
Root directory        $             \                /
Parent directory      ^             ..               ..
Current Directory     @             .                .
Library Directory     %             
User Directory        &             %USERPROFILE%    $HOME$
Wild Character        #             ?                ?
Wild String           *             *                *

The simplest conversion is between UNIX and DOS pathnames; other than the '/' and '\' directory separators all other characters used are identical. Conversion is simply an issue of swapping '/' and '\' characters.

The BBC uses '#' as a single-character wildcard, DOS and UNIX use '?'. Conversely, on the BBC '?' is an ordinary character and on DOS and UNIX '#' is an ordinary character, so thay are swapped.

The BBC uses '.' as a directory separator and '/' as an extension separator; DOS and UNIX use '.' as an extension separator, DOS uses '\' as a directory separator and UNIX uses '/'. '.' and '/' are swapped when converting between BBC and UNIX. BBC '.', '\', '/' are swapped with DOS '\', '/', '.' when converting between BBC and DOS.

The functions in this library do not provide a perfect translation mapping, the BBC has more illegal filename characters than other platforms which makes a bidirection mapping difficult. For example, the DOS filename "$TEMP$.$$$" cannot be translated to a BBC filename and then have a perfect one-to-one mapping back to the same DOS filename. If support for such more complex pathnames are needed a more advanced Filename library should be used.


     REM > BLib.Filename 1.02 25-Jan-2011
     REM Convert between BBC, DOS/Windows and UNIX/ZIP/URL filenames
     REM!Keep FNfn_frombbc(), FNfn_tobbc(), FNfn_unixtodos(), FNfn_dostounix()
     REM Swap between BBC and Unix/Zip filenames
     REM ---------------------------------------
     DEFFNfn_zip(B$):LOCALB%:A$="#?./$<^>&+@=%; "
     FOR A%=1 TO LEN B$:B%=INSTR(A$,MID$(B$,A%,1))-1
       IF B%>TRUE:B$=LEFT$(B$,A%-1)+MID$(A$+"_",(B%EOR1)+1,1)+MID$(B$,A%+1)
       IFB%=35ORB%=63:A$=LEFT$(A$,A%-1)+CHR$(B%EOR28)+MID$(A$,A%+1) :REM # ?
       IFB%=61ORB%=64:A$=LEFT$(A$,A%-1)+CHR$(B%EOR125)+MID$(A$,A%+1):REM = @
       IFB%=36ORB%=60:A$=LEFT$(A$,A%-1)+CHR$(B%EOR24)+MID$(A$,A%+1) :REM < $
       IFB%=62ORB%=94:A$=LEFT$(A$,A%-1)+CHR$(B%EOR96)+MID$(A$,A%+1) :REM > ^
       REM IFB%=ASC"&":A$=LEFT$(A$,A%-1)+"%HOME%"+MID$(A$,A%+1)
     DEFFNfn_fromunix(A$):IF(os%AND-24)=8:=A$ ELSE IF(os%AND-24)=0:=FNfn_unixtobbc(A$):DEF:FNfn_dostounix()
     DEFFNfn_fromdos(A$):IF(os%AND-32):=A$ ELSE IF(os%AND-24)=0:=FNfn_dostobbc(A$):DEF:FNfn_dostounix()
     DEFFNfn_tounix(A$):IF(os%AND-24)=8:=A$ ELSE IF(os%AND-24)=0:=FNfn_bbctounix(A$):DEF:FNfn_dostounix()
     DEFFNfn_todos(A$):IF(os%AND-32):=A$ ELSE IF(os%AND-24)=0:=FNfn_bbctodos(A$):DEF:FNfn_dostounix()
     LOCAL A%,B%:A%=0:REPEAT:A%=A%+1:B%=ASCMID$(A$,A%,1):IFB%=ASC"/"ORB%=ASC"\":A$=LEFT$(A$,A%-1)+CHR$(B%EOR115)+MID$(A$,A%+1)
       IFB%=35ORB%=63:A$=LEFT$(A$,A%-1)+CHR$(B%EOR28)+MID$(A$,A%+1):REM # ?
       REM IFMID$(A$,A%,6)="%HOME%":A$=LEFT$(A$,A%-1)+"&"+MID$(A$,A%+6):B%=0

Requirements and Dependancies

The FNfn_from*() and FNfn_to*() functions require the global variable os% set to the host the program is running on, such as by calling FNOS_GetEnv in the ProgEnv library or with


All the other functions have no external requirements.


The code is rather untidy and can be tidied up.

See also

JGH, 25 April 2011 Jgharston (talk) 06:24, 3 April 2018 (CEST)