[← 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