AmigaDOS Paths and File Names

An AmigaDOS path has two parts: an optional device, volume or assign name ending in a colon (:), and a string that the responsible handler interprets. dos.library interprets only the first part: it looks the name up in the device list (see The AmigaDOS Device List) to find the handler, or uses the current directory of the process when there is no name. Everything after the colon goes to the handler unchanged. The Fast File System reads it as a location in its directory tree; the CON-Handler reads it as the size and options of the window to open. This article covers the path syntax, character set and case rules, the length limits, the system-defined device names, and the dos.library functions that work on paths (chapter 4 and §7.3 of the 2024 ROM Kernel Reference Manual).

Path syntax

Path Meaning
DH0:Work/Text Absolute: Text in directory Work of the root of DH0:
:S Absolute: S in the root directory of the volume holding the current directory
Work/Text Relative to the current directory
(empty string) The current directory itself
/ The parent of the current directory
//Top/Hi Hi in Top, two directories above the current directory
  • A path with a colon is absolute; without one it is relative to the current directory. A colon with no name in front is still absolute, relative to the root of the volume that holds the current directory.
  • If the current directory is ZERO, the process's default file system (unless changed, the one that booted the system) is used.
  • Components are separated by a forward slash /. This is a convention of all AmigaOS file systems, not something dos.library enforces, but the path functions below and the pattern matcher depend on it.
  • There is no . or ... The current directory is the empty string; the parent is an isolated / without a preceding component. The parent of the root directory is the root itself.
  • * is not a wildcard: as a complete path it means the console of the process. Open() and most other functions take wildcard characters literally.

How a file system walks a path

The manual recommends that every file system follow this algorithm (§4.6): walk the path component by component; an isolated / without a preceding component means "go to the parent"; a component followed by / means "enter this directory and continue"; the last component names the object. Handlers that are not file systems can use other conventions, which is why tricks such as splitting long paths (below) only work on file systems.

Case sensitivity and character encoding

  • Device, volume and assign names are always case-insensitive. dos.library compares them with the help of the current locale and assumes ISO-Latin-1.
  • Whether the rest of the path is case-sensitive depends on the file system. File systems should be case-insensitive. The original, non-international FFS variants (DOS\0, DOS\1) do not compare characters with the high bit set correctly; prefer the international variants (see AmigaDOS Filesystem On-Disk Structures).
  • The pattern matcher and other support functions also assume case-insensitive ISO-Latin-1 names, so what a file system considers the same name can differ from what dos.library considers a match.
  • On FFS volumes a name can contain every printable ISO-Latin-1 character (0x20–0x7e, 0xa0–0xff) except : and /. Other file systems can impose further restrictions from their native systems.
  • Never use control characters (C0 0x01–0x1f, C1 0x7f–0x9f). The pattern matcher uses them internally as wildcard tokens and misbehaves, and the console treats them as control sequences. The FFS rejects all control characters; the RAM-Handler (probably by mistake) accepts the C1 range 0x80–0x9f. NUL (0x00) cannot be used at all, since it terminates C strings.

Characters to avoid

These characters are legal in names but have a special meaning to the Shell, ReadArgs() or the pattern matcher, and make files hard to reach from scripts:

Character Why
* Alone, the current console; also the Shell escape character inside double quotes
> < \| Shell redirection and pipe operators; need quoting
space (0x20) Separates commands and arguments; needs quoting
= ReadArgs() separator between a keyword and its value; needs double quotes
# ? [ ] ' ~ ( ) % Pattern matcher syntax (' is its escape character); there is no way to know whether a given command passes its arguments through the matcher

Workbench handles all of these without quoting; the only name that cannot work there is a lone *, because dos.library (not the Shell) gives it a meaning.

Length limits

Item Limit Reason
Path passed to most dos.library functions 255 characters Converted to a BSTR, whose length is one byte
Device, volume and assign names 30 characters GetDeviceProc() and the Assign command cannot handle more
File and directory names on FFS 30, 54 or 106 characters, by FFS variant On-disk format; 106 is also the limit of the FileInfoBlock
Paths handled by the §7.3 functions and ReadLink() no 255 limit Never converted to a BSTR
  • The FFS limits are one less than the available buffer (32, 56 or 108 bytes) on purpose: one byte is kept for the BSTR length and a terminating NUL. (One passage of the manual, §7.1, gives 56 for the middle variant; §4.2 and table 8.3 give 54.)
  • Names that are too long are usually truncated silently, with no error. A file system can thus clip a trailing .info, and Workbench no longer recognizes the file as an icon. icon.library and workbench.library avoid such names and check the created objects.
  • Paths longer than 255 characters give unpredictable results with Open(), Lock() and similar calls. Work around the limit with a lock plus a relative path, as shown in Opening an overlong path.

Devices and pseudo-devices

Table 4.1: devices that the ROM always creates, even without a boot volume.

Device Description
DF0: First floppy drive (DF1:–DF3: for further drives)
PRT: Printer
PAR: Parallel port
SER: Serial port
CON: Line-interactive console
RAW: Character-based console
PIPE: Pipe between processes (ROM-mounted from V47, needed by the Boot Shell)
RAM: RAM-based file system

SER:, PAR:, PRT: and PIPE: are mounted by the ROM, but their handlers live on disk and are loaded when first used. Hard disk partitions get the names stored in the Rigid Disk Block, by convention DH0: and up; their volume names should differ from their device names.

Table 4.2: names with a special meaning that do not correspond to a handler.

Name Meaning
* The console of the current process. Used without a colon or file name. Without a console (e.g. a Workbench program), V36+ opens NIL: instead; earlier versions failed.
CONSOLE: The default console of the process (V36). Takes a colon and an optional job name after it.
NIL: The data sink, maintained by dos.library itself without a handler. Writes vanish, reads return end-of-file. No file name may follow.

Prefer * to CONSOLE:. * identifies the process as part of its Shell job. CONSOLE: denotes the job that was started with the Shell, so output to it can block the process on consoles with job control (the ROM CON-Handler has none, some third-party consoles do).

Custom device names added with Mount may be chosen freely, as long as they do not clash with these names or the system assigns.

Volumes

A volume name identifies a particular medium (a floppy, a partition) regardless of the drive it is in. dos.library tells volumes of the same name apart by their creation date (dol_VolumeDate).

System assigns

Created by dos.library during bootstrap (table 4.3): C, L, S, LIBS, DEVS, FONTS and SYS are regular assigns to the boot volume; ENVARC (V36) is a late-binding assign to SYS:Prefs/Env-Archive. The Startup-sequence then adds ENV, T, CLIPS, KEYMAPS, PRINTERS, REXX, LOCALE and CLASSES; ENV, REXX, LOCALE and CLASSES appeared in V36, V38 and V39. See AmigaDOS Assigns.

PROGDIR: (V36) is a pseudo-assign handled inside dos.library and not in the device list: the directory the running program was loaded from. It does not exist for programs that were not loaded from disk (ROM-resident or resident commands). Accessing it then brings up an unhelpful "insert volume PROGDIR" requester, a defect still present in V47.

Opening an overlong path

The manual's OpenFromLongName() (§4.6.1) opens files whose path exceeds 255 characters. It walks the path with SplitName(), locks each directory, and makes it the current directory, so that every call only ever sees one component. It works only when the handler is a file system that follows the path convention above.

#include <dos/dos.h>
#include <proto/dos.h>

BPTR OpenFromLongName(UBYTE *name, LONG mode)
{
    LONG pos  = 0;
    BPTR file = 0;
    BPTR old  = -1L;              /* never a valid lock */
    UBYTE buffer[108 + 32];       /* one component plus a device name */

    do {
        pos = SplitName(name, '/', buffer, pos, sizeof(buffer));
        if (pos < 0) {
            /* No separator left: open the last component. */
            file = Open(buffer, mode);
            break;
        } else {
            BPTR lock;
            /* Lock the partial path; an empty component ("//")
               means the parent directory. */
            if (!(lock = Lock(*buffer ? buffer : "/", SHARED_LOCK)))
                break;

            lock = CurrentDir(lock);          /* rotate directories */

            if (old >= 0)
                UnLock(lock);                 /* our previous directory */
            else
                old = lock;                   /* the caller's directory */
        }
    } while (1);

    /* Restore the original current directory, if changed.
       None of these calls touch IoErr(). */
    if (old >= 0)
        UnLock(CurrentDir(old));

    return file;
}

Path functions

The functions of §7.3 work on the path string inside dos.library, without asking a file system. Consequently they have no 255-character limit, and they impose the dos.library syntax: / as separator and an isolated / for the parent.

LONG   NameFromLock(BPTR lock, STRPTR buffer, LONG len);              /* V36 */
LONG   NameFromFH(BPTR fh, STRPTR buffer, LONG len);                  /* V36 */
BOOL   AddPart(STRPTR dirname, CONST_STRPTR filename, ULONG size);    /* V36 */
STRPTR FilePart(CONST_STRPTR path);                                   /* V36 */
STRPTR PathPart(CONST_STRPTR path);                                   /* V36 */
WORD   SplitName(CONST_STRPTR name, ULONG separator, STRPTR buf,
                 LONG oldpos, LONG size);                             /* V36 */
Function Does On failure IoErr() on success Bugs and caveats
NameFromLock() 2.0+ Absolute path of a locked object 0; ERROR_LINE_TOO_LONG if len is too small (result still NUL-terminated) undefined ZERO gives "SYS:", wrong if SYS: or the default file system was changed; can bring up a requester if the volume is not inserted
NameFromFH() 2.0+ Absolute path of an open file (also exclusive ones) 0; ERROR_LINE_TOO_LONG set to 0 —
AddPart() 2.0+ Appends a relative or absolute path to dirname in place 0, ERROR_LINE_TOO_LONG, dirname unchanged not altered V36 mishandles leading / and absolute paths; fixed in V37. Purely textual: does not check that objects exist
FilePart() 2.0+ Pointer to the last component cannot fail not touched Returns a pointer to / if the path ends in two or more slashes
PathPart() 2.0+ Pointer to the end of the next-to-last component cannot fail not touched Unlike FilePart(), does not skip a trailing /
SplitName() 2.0+ Copies the component at oldpos into buf, returns the next position or -1 at the end — never set, even if buf is too small V37 and earlier lose the last character of a component that fills buf and is followed by the separator; fixed in V39

Both NameFromLock() and NameFromFH() return the path of the link target, not of the link through which the object was reached, and always an absolute path.

Note: the NDK 3.2 autodoc for NameFromLock() states that, except for ZERO, it requires a shared lock and returns an error for an exclusive one.

Note: the NDK 3.2 autodoc for SplitName() gives a workaround for V36/V37: pass a buffer one byte larger than normal and set buf[size-2] to NUL.

FilePart() and PathPart() together split a path into directory and name. Writing a NUL at the PathPart() pointer leaves the containing directory:

UBYTE path[256] = "Work:Projects/Demo/main.c";
UBYTE full[256];
STRPTR name = FilePart(path);   /* "main.c" */

*PathPart(path) = '\0';         /* path is now "Work:Projects/Demo" */

strcpy(full, path);
if (!AddPart(full, "/include/demo.h", sizeof(full))) {
    /* ERROR_LINE_TOO_LONG; full is unchanged */
}
/* full is "Work:Projects/include/demo.h": the leading "/" went up one level */

See Also


Sources: Thomas Richter, Amiga ROM Kernel Reference Manual: AmigaDOS (2024, v1.27), ch. 4, §4.1–4.6.1, §7.3–7.3.6 (pp. 21–29, 81–84); NDK 3.2 R4 dos.doc (NameFromLock, SplitName, AddPart), clib/dos_protos.h Updated: 2026-11-13