[← Home](../README.md) · [Libraries](README.md)
# utility.library — TagItems, Hooks, Date Utilities
## Overview
`utility.library` (OS 2.0+) provides the universal tag-based parameter passing system, callback hooks, and date/time utilities used throughout AmigaOS. Nearly every OS 2.0+ API uses TagItem lists for extensible parameter passing — understanding this library is essential.
---
## TagItem System
### Structure
```c
/* utility/tagitem.h — NDK39 */
struct TagItem {
ULONG ti_Tag; /* tag identifier */
ULONG ti_Data; /* tag value (ULONG — often cast from pointer) */
};
```
### Special Tags
| Tag | Value | Purpose |
|---|---|---|
| `TAG_DONE` | 0 | Terminates a tag list — must be the last entry |
| `TAG_IGNORE` | 1 | Skip this tag (placeholder) |
| `TAG_MORE` | 2 | `ti_Data` = pointer to another TagItem array (chain lists) |
| `TAG_SKIP` | 3 | Skip next `ti_Data` tags in the list |
| `TAG_USER` | `1<<31` | User-defined tags start at this value |
### How Tag Lists Work
```mermaid
flowchart LR
subgraph "Tag List (array)"
T1["SA_Width
640"] --> T2["SA_Height
480"]
T2 --> T3["SA_Depth
8"]
T3 --> TM["TAG_MORE
→ extraTags"]
end
subgraph "Chained List"
E1["SA_Title
'My Screen'"] --> E2["TAG_DONE
0"]
end
TM --> E1
style TM fill:#fff9c4,stroke:#f9a825,color:#333
style E2 fill:#ffcdd2,stroke:#c62828,color:#333
```
```c
/* Typical usage: */
struct Screen *scr = OpenScreenTags(NULL,
SA_Width, 640,
SA_Height, 480,
SA_Depth, 8,
SA_Title, (ULONG)"My Screen",
SA_ShowTitle, TRUE,
TAG_DONE);
/* Tag list as array: */
struct TagItem tags[] = {
{ SA_Width, 640 },
{ SA_Height, 480 },
{ SA_Depth, 8 },
{ TAG_DONE, 0 }
};
struct Screen *scr = OpenScreenTagList(NULL, tags);
```
### Tag Utility Functions
| Function | Description |
|---|---|
| `FindTagItem(tag, tagList)` | Find first matching tag; returns `TagItem *` or NULL |
| `GetTagData(tag, default, tagList)` | Get tag value, or default if tag not found |
| `NextTagItem(&tagListPtr)` | Iterator — handles TAG_MORE, TAG_SKIP, TAG_IGNORE transparently |
| `TagInArray(tag, array)` | Check if a tag ID is in a ULONG array |
| `FilterTagItems(tagList, filter, logic)` | Remove/keep tags matching a filter array |
| `CloneTagItems(tagList)` | Allocate a copy of the entire tag list |
| `FreeTagItems(tagList)` | Free a cloned tag list |
| `MapTags(tagList, mapList, flags)` | Remap tag IDs (for converting between APIs) |
| `PackBoolTags(initialFlags, tagList, boolMap)` | Convert boolean tags to a flags word |
### Iterating a Tag List
```c
/* The correct way to iterate — handles all special tags: */
struct TagItem *tag;
struct TagItem *tstate = tagList;
while ((tag = NextTagItem(&tstate)))
{
switch (tag->ti_Tag)
{
case MY_WIDTH: width = tag->ti_Data; break;
case MY_HEIGHT: height = tag->ti_Data; break;
case MY_TITLE: title = (char *)tag->ti_Data; break;
}
}
```
> [!IMPORTANT]
> **Never iterate tag lists manually** with `for` loops. Always use `NextTagItem()` — it correctly handles `TAG_MORE` chains, `TAG_SKIP`, and `TAG_IGNORE`. Manual iteration will break on chained or filtered lists.
---
## Hook System
Hooks provide a standardised callback mechanism used throughout AmigaOS — Intuition, BOOPSI, layers.library, and locale.library all use them.
### Structure
```c
/* utility/hooks.h */
struct Hook {
struct MinNode h_MinNode; /* for linking into lists */
ULONG (*h_Entry)(void); /* assembler entry point */
ULONG (*h_SubEntry)(void); /* C function pointer */
APTR h_Data; /* user data */
};
```
### Register Convention
When a hook is called, registers are set up as:
- **A0** = pointer to the `Hook` itself
- **A2** = the "object" (context-dependent)
- **A1** = the "message" (context-dependent)
```c
/* SAS/C / GCC with register args: */
ULONG __saveds __asm MyHookFunc(
register __a0 struct Hook *hook,
register __a2 APTR object,
register __a1 APTR message)
{
struct MyData *data = (struct MyData *)hook->h_Data;
/* ... process callback ... */
return 0;
}
/* Initialize the hook: */
struct Hook myHook;
myHook.h_Entry = (HOOKFUNC)HookEntry; /* utility.library glue */
myHook.h_SubEntry = (HOOKFUNC)MyHookFunc;
myHook.h_Data = myPrivateData;
```
### Common Hook Uses
| API | Object (A2) | Message (A1) |
|---|---|---|
| BOOPSI `OM_SET` | BOOPSI object | `opSet` message |
| `InstallLayerHook` | Layer | `struct BackFillMessage` |
| Locale `FormatString` | — | Format data |
| `DoMethod` (MUI) | MUI object | Method message |
---
## Date Utilities
```c
#include
struct ClockData {
UWORD sec; /* 0–59 */
UWORD min; /* 0–59 */
UWORD hour; /* 0–23 */
UWORD mday; /* 1–31 */
UWORD month; /* 1–12 */
UWORD year; /* 1978+ */
UWORD wday; /* 0=Sunday */
};
/* Convert Amiga timestamp to date components: */
struct ClockData cd;
Amiga2Date(seconds, &cd);
Printf("%02d/%02d/%04d %02d:%02d:%02d\n",
cd.mday, cd.month, cd.year,
cd.hour, cd.min, cd.sec);
/* Convert date to Amiga timestamp: */
cd.year = 2024; cd.month = 3; cd.mday = 15;
cd.hour = 14; cd.min = 30; cd.sec = 0;
ULONG secs = Date2Amiga(&cd);
/* Validate a date and get timestamp: */
ULONG secs = CheckDate(&cd); /* returns 0 if invalid */
```
> [!NOTE]
> The Amiga epoch is **January 1, 1978 00:00:00 UTC**. To convert to/from Unix timestamps (epoch 1970-01-01), add/subtract **252,460,800** seconds (8 years + 2 leap days).
---
## References
- NDK39: `utility/tagitem.h`, `utility/hooks.h`, `utility/date.h`
- ADCD 2.1: utility.library autodocs
- See also: [boopsi.md](../09_intuition/boopsi.md) — BOOPSI uses hooks extensively