amiga-bootcamp/07_dos/file_io.md
2026-04-26 14:46:18 -04:00

285 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[← Home](../README.md) · [AmigaDOS](README.md)
# 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](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)` | D1D4 | D0=blocks read (buffered) |
| 336 | `FWrite(fh, buf, bsize, n)` | D1D4 | D0=blocks written (buffered) |
| 348 | `FGets(fh, buf, len)` | D1D3 | D0=buf or NULL |
| 354 | `FPuts(fh, str)` | D1D2 | D0=0 or EOF |
| 360 | `Flush(fh)` | D1=handle | D0=BOOL |
| 366 | `SetVBuf(fh, buf, type, size)` | D1D4 | D0=0 or 1 |
---
## Access Modes
```c
/* 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
```c
#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)` then `size = Seek(fh, 0, OFFSET_BEGINNING)`.
---
## File Handle Structure
The returned handle is a BPTR to a `struct FileHandle`:
```c
/* 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) */
```
```c
/* 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()`:
```c
/* 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
```c
/* 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 `Printf` uses `%ld` for integers, not `%d`. The `%d` format is undefined. This is a common bug source when porting from Unix/ANSI C.
---
## File Information — ExamineFH, ExAll
```c
/* 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
```c
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
```c
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
```c
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
```c
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
```c
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](error_handling.md) — full error code list
- [packet_system.md](packet_system.md) — how I/O translates to handler packets