8.6 KiB
File I/O — Open, Close, Read, Write, Seek, Async I/O
Overview
AmigaDOS file I/O is synchronous from the caller's perspective — each call blocks until the filesystem handler completes the operation. All functions use BPTR file handles and communicate errors via IoErr(). Internally, every I/O call is translated into a DosPacket sent to the filesystem handler process (see packet_system.md).
Core Functions
| LVO | Function | Registers | Returns |
|---|---|---|---|
| −30 | Open(name, mode) |
D1=name, D2=mode | D0=BPTR handle (0=fail) |
| −36 | Close(fh) |
D1=handle | D0=BOOL |
| −42 | Read(fh, buf, len) |
D1=handle, D2=buf, D3=len | D0=actual bytes (−1=error) |
| −48 | Write(fh, buf, len) |
D1=handle, D2=buf, D3=len | D0=actual bytes (−1=error) |
| −66 | Seek(fh, pos, mode) |
D1=handle, D2=pos, D3=mode | D0=old position (−1=error) |
| −330 | FRead(fh, buf, bsize, n) |
D1–D4 | D0=blocks read (buffered) |
| −336 | FWrite(fh, buf, bsize, n) |
D1–D4 | D0=blocks written (buffered) |
| −348 | FGets(fh, buf, len) |
D1–D3 | D0=buf or NULL |
| −354 | FPuts(fh, str) |
D1–D2 | D0=0 or EOF |
| −360 | Flush(fh) |
D1=handle | D0=BOOL |
| −366 | SetVBuf(fh, buf, type, size) |
D1–D4 | D0=0 or −1 |
Access Modes
/* dos/dos.h — NDK39 */
#define MODE_READWRITE 1004 /* open existing, read+write */
#define MODE_OLDFILE 1005 /* open existing, read only */
#define MODE_NEWFILE 1006 /* create new (truncate if exists) */
| Mode | If file exists | If file doesn't exist |
|---|---|---|
MODE_OLDFILE |
Opens for reading | Fails — IoErr() = ERROR_OBJECT_NOT_FOUND |
MODE_NEWFILE |
Truncates to 0, opens for writing | Creates new file |
MODE_READWRITE |
Opens at current size, read+write | Creates new file |
Seek Modes
#define OFFSET_BEGINNING -1 /* from start of file */
#define OFFSET_CURRENT 0 /* from current position */
#define OFFSET_END 1 /* from end of file */
Note
:
Seek()returns the old position (before seeking), not the new one. To get file size:Seek(fh, 0, OFFSET_END)thensize = Seek(fh, 0, OFFSET_BEGINNING).
File Handle Structure
The returned handle is a BPTR to a struct FileHandle:
/* dos/dosextens.h */
struct FileHandle {
struct Message *fh_Link; /* +$00: exec message for reply */
struct MsgPort *fh_Interactive; /* +$04: non-NULL if console device */
struct MsgPort *fh_Type; /* +$08: handler process MsgPort */
BPTR fh_Buf; /* +$0C: I/O buffer (BPTR) */
LONG fh_Pos; /* +$10: current position in buffer */
LONG fh_End; /* +$14: end of valid data in buffer */
LONG fh_Funcs; /* +$18: unused (was BCPL function) */
LONG fh_Func2; /* +$1C: unused */
LONG fh_Func3; /* +$20: unused */
LONG fh_Args; /* +$24: FH_Arg1 — passed to handler */
BPTR fh_Arg2; /* +$28: handler-specific */
};
/* sizeof(struct FileHandle) = 44 bytes ($2C) */
/* To access as a C pointer: */
struct FileHandle *fhp = BADDR(handle); /* BADDR = (ptr << 2) */
/* Check if interactive (console): */
if (IsInteractive(fh))
Printf("Connected to a console\n");
I/O Buffering
AmigaDOS uses per-handle buffering (OS 2.0+). Default buffer is 512 bytes. Control with SetVBuf():
/* Make a file fully buffered with 8 KB buffer: */
SetVBuf(fh, NULL, BUF_FULL, 8192);
/* Line-buffered (flush on newline — useful for consoles): */
SetVBuf(fh, NULL, BUF_LINE, 1024);
/* Unbuffered (every write goes to handler immediately): */
SetVBuf(fh, NULL, BUF_NONE, 0);
/* Manually flush: */
Flush(fh);
| Buffer Type | Constant | Behavior |
|---|---|---|
BUF_FULL |
1 | Flush when buffer fills |
BUF_LINE |
0 | Flush on newline or buffer full |
BUF_NONE |
−1 | No buffering — direct I/O |
Standard I/O Handles
/* Get the current process's stdin/stdout/stderr: */
BPTR in = Input(); /* stdin — from CLI or WB */
BPTR out = Output(); /* stdout */
BPTR err = ErrorOutput(); /* stderr (OS 3.0+ only) */
/* Write to stdout: */
FPuts(Output(), "Hello from AmigaDOS\n");
/* Printf — formatted output to stdout: */
Printf("Count: %ld, Name: %s\n", count, name);
/* Note: Printf uses %ld (not %d) — AmigaDOS always uses LONG */
Important
AmigaDOS
Printfuses%ldfor integers, not%d. The%dformat is undefined. This is a common bug source when porting from Unix/ANSI C.
File Information — ExamineFH, ExAll
/* Get file metadata from a handle: */
struct FileInfoBlock *fib = AllocDosObject(DOS_FIB, NULL);
if (ExamineFH(fh, fib))
{
Printf("Name: %s\n", fib->fib_FileName);
Printf("Size: %ld\n", fib->fib_Size);
Printf("Type: %ld\n", fib->fib_DirEntryType);
/* > 0 = directory, < 0 = file */
}
FreeDosObject(DOS_FIB, fib);
FileInfoBlock Structure
struct FileInfoBlock {
LONG fib_DiskKey; /* block number on disk */
LONG fib_DirEntryType; /* >0=dir, <0=file */
char fib_FileName[108]; /* null-terminated name */
LONG fib_Protection; /* RWED protection bits */
LONG fib_EntryType; /* same as DirEntryType */
LONG fib_Size; /* file size in bytes */
LONG fib_NumBlocks; /* blocks consumed */
struct DateStamp fib_Date; /* modification date */
char fib_Comment[80]; /* file comment string */
UWORD fib_OwnerUID; /* owner (multiuser) */
UWORD fib_OwnerGID; /* group */
};
Practical Patterns
Copy a File
BOOL CopyFile(CONST_STRPTR src, CONST_STRPTR dst)
{
BPTR in = Open(src, MODE_OLDFILE);
if (!in) return FALSE;
BPTR out = Open(dst, MODE_NEWFILE);
if (!out) { Close(in); return FALSE; }
UBYTE buf[4096];
LONG n;
while ((n = Read(in, buf, sizeof(buf))) > 0)
{
if (Write(out, buf, n) != n)
{
Close(in); Close(out);
return FALSE; /* write error */
}
}
Close(out);
Close(in);
return (n == 0); /* 0=EOF=success, -1=error */
}
Determine File Size
LONG GetFileSize(BPTR fh)
{
LONG oldpos = Seek(fh, 0, OFFSET_END); /* seek to end */
LONG size = Seek(fh, oldpos, OFFSET_BEGINNING); /* seek back */
return size;
}
/* Or with ExamineFH (no seeking needed): */
struct FileInfoBlock fib;
ExamineFH(fh, &fib);
LONG size = fib.fib_Size;
Read Entire File into Memory
APTR LoadFileToRAM(CONST_STRPTR path, ULONG *sizeOut)
{
BPTR fh = Open(path, MODE_OLDFILE);
if (!fh) return NULL;
/* Get size */
Seek(fh, 0, OFFSET_END);
LONG size = Seek(fh, 0, OFFSET_BEGINNING);
APTR buf = AllocVec(size, MEMF_ANY);
if (buf)
{
if (Read(fh, buf, size) != size)
{
FreeVec(buf);
buf = NULL;
}
}
Close(fh);
*sizeOut = size;
return buf;
}
Error Checking
LONG err = IoErr(); /* returns last DOS error code */
/* Human-readable error message: */
PrintFault(err, "Operation failed");
/* Output: "Operation failed: object not found" */
/* Or get error string into buffer: */
Fault(err, "Error", buf, sizeof(buf));
Common Error Codes
| Code | Constant | Meaning |
|---|---|---|
| 103 | ERROR_NO_FREE_STORE |
Out of memory |
| 202 | ERROR_OBJECT_IN_USE |
File is locked by another process |
| 203 | ERROR_OBJECT_EXISTS |
File already exists |
| 204 | ERROR_DIR_NOT_FOUND |
Path component not found |
| 205 | ERROR_OBJECT_NOT_FOUND |
File not found |
| 209 | ERROR_ACTION_NOT_KNOWN |
Handler doesn't support this action |
| 210 | ERROR_INVALID_COMPONENT_NAME |
Bad filename character |
| 212 | ERROR_OBJECT_WRONG_TYPE |
Expected file, got directory (or vice versa) |
| 214 | ERROR_DISK_FULL |
No space on volume |
| 216 | ERROR_DELETE_PROTECTED |
File has delete-protection bit |
| 218 | ERROR_WRITE_PROTECTED |
Disk is write-protected |
| 219 | ERROR_SEEK_ERROR |
Invalid seek position |
| 221 | ERROR_DISK_FULL |
Volume full |
| 225 | ERROR_NOT_A_DOS_DISK |
Unrecognised filesystem |
| 232 | ERROR_NO_MORE_ENTRIES |
ExNext — end of directory |
References
- NDK39:
dos/dos.h,dos/dosextens.h,dos/stdio.h - ADCD 2.1:
Open,Close,Read,Write,Seek,SetVBuf,FRead - error_handling.md — full error code list
- packet_system.md — how I/O translates to handler packets