

<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.amigaos.net/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Fredrik+Wikstrom</id>
	<title>AmigaOS Documentation Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.amigaos.net/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Fredrik+Wikstrom"/>
	<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/wiki/Special:Contributions/Fredrik_Wikstrom"/>
	<updated>2026-04-11T22:47:22Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Date_Functions&amp;diff=11115</id>
		<title>Date Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Date_Functions&amp;diff=11115"/>
		<updated>2020-05-22T13:31:05Z</updated>

		<summary type="html">&lt;p&gt;Fredrik Wikstrom: Fixed Printf() statements in example code&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Date Functions =&lt;br /&gt;
&lt;br /&gt;
To ease date-related calculations, the utility library has some functions to convert a date, specified in a ClockData structure, in the number of seconds since 00:00:00 01-Jan-78 and vice versa.&lt;br /&gt;
&lt;br /&gt;
To indicate the date, the ClockData structure (in &amp;amp;lt;utility/date.h&amp;amp;gt;) is used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ClockData&lt;br /&gt;
{&lt;br /&gt;
    UWORD sec;     /* seconds (0 - 59) */&lt;br /&gt;
    UWORD min;     /* minutes (0 - 59) */&lt;br /&gt;
    UWORD hour;    /* hour (0 - 23) */&lt;br /&gt;
    UWORD mday;    /* day of the month (1 - 31) */&lt;br /&gt;
    UWORD month;   /* month of the year (1 - 12) */&lt;br /&gt;
    UWORD year;    /* 1978 - */&lt;br /&gt;
    UWORD wday;    /* day of the week (0 - 6, where 0 is Sunday) */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following functions are available to operate on ClockData:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Utility Library Date Functions&lt;br /&gt;
| Amiga2Date() || Calculate the date from the specified timestamp (in seconds).&lt;br /&gt;
|-&lt;br /&gt;
| CheckDate() || Check the legality of a date.&lt;br /&gt;
|-&lt;br /&gt;
| Date2Amiga() || Calculate the timestamp from the specified date.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Amiga2Date() takes a number of seconds from 01-Jan-78 as argument and fills in the supplied ClockData structure with the date and time.&lt;br /&gt;
&lt;br /&gt;
CheckDate() checks if the supplied ClockData structure is valid, and returns the number of seconds from 01-Jan-78 if it is. Note that this function currently does not take the supplied day of the week in account.&lt;br /&gt;
&lt;br /&gt;
Date2Amiga() takes a ClockData structure as argument and returns the number of seconds since 01-Jan-78. The supplied ClockData structure &#039;&#039;must&#039;&#039; be valid, since no checking is done.&lt;br /&gt;
&lt;br /&gt;
The following example shows various uses of the utility library date functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// a2d.c&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/datetime.h&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/timer.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// Note these three libraries are opened by newlib startup code.&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;proto/timer.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
struct TimerIFace *ITimer = NULL;&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct MsgPort *mp = IExec-&amp;gt;AllocSysObject(ASOT_PORT, NULL);&lt;br /&gt;
 &lt;br /&gt;
  if (mp != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    struct TimeRequest *tr = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
      ASOIOR_Size, sizeof(struct TimeRequest),&lt;br /&gt;
      ASOIOR_ReplyPort, mp,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
 &lt;br /&gt;
    if (tr != NULL)&lt;br /&gt;
    {&lt;br /&gt;
      if (!IExec-&amp;gt;OpenDevice(&amp;quot;timer.device&amp;quot;, UNIT_VBLANK, (struct IORequest *)tr, 0))&lt;br /&gt;
      {&lt;br /&gt;
        struct Library *TimerBase = (struct Library *)tr-&amp;gt;Request.io_Device;&lt;br /&gt;
        ITimer = (struct TimerIFace*)IExec-&amp;gt;GetInterface(TimerBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
 &lt;br /&gt;
        if (ITimer != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          struct TimeVal tv;&lt;br /&gt;
          ITimer-&amp;gt;GetSysTime(&amp;amp;tv);&lt;br /&gt;
 &lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;GetSysTime():\t%lu %lu\n&amp;quot;, tv.Seconds, tv.Microseconds);&lt;br /&gt;
 &lt;br /&gt;
          struct ClockData clockdata;&lt;br /&gt;
          IUtility-&amp;gt;Amiga2Date(tv.Seconds, &amp;amp;clockdata);&lt;br /&gt;
 &lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Amiga2Date():  sec %lu min %lu hour %lu\n&amp;quot;,&lt;br /&gt;
            clockdata.sec, clockdata.min, clockdata.hour);&lt;br /&gt;
 &lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;               mday %lu month %lu year %lu wday %lu\n&amp;quot;,&lt;br /&gt;
            clockdata.mday, clockdata.month, clockdata.year, clockdata.wday);&lt;br /&gt;
 &lt;br /&gt;
          uint32 seconds = IUtility-&amp;gt;CheckDate(&amp;amp;clockdata);&lt;br /&gt;
 &lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;CheckDate():\t%lu\n&amp;quot;, seconds);&lt;br /&gt;
 &lt;br /&gt;
          seconds = IUtility-&amp;gt;Date2Amiga(&amp;amp;clockdata);&lt;br /&gt;
 &lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Date2Amiga():\t%lu\n&amp;quot;, seconds);&lt;br /&gt;
 &lt;br /&gt;
          IExec-&amp;gt;DropInterface((struct Interface*)ITimer);&lt;br /&gt;
        }&lt;br /&gt;
 &lt;br /&gt;
        IExec-&amp;gt;CloseDevice((struct IORequest *)tr);&lt;br /&gt;
      }&lt;br /&gt;
 &lt;br /&gt;
      IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, tr);&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, mp);&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Date Function Reference =&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the utility library functions which pertain to date conversion. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| CheckDate()&lt;br /&gt;
| Check the legality of a date.&lt;br /&gt;
|-&lt;br /&gt;
| Amiga2Date()&lt;br /&gt;
| Calculate the date from a specified timestamp.&lt;br /&gt;
|-&lt;br /&gt;
| Date2Amiga()&lt;br /&gt;
| Calculate the timestamp from a specified date.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Fredrik Wikstrom</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Date_Functions&amp;diff=11114</id>
		<title>Date Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Date_Functions&amp;diff=11114"/>
		<updated>2020-05-17T21:08:53Z</updated>

		<summary type="html">&lt;p&gt;Fredrik Wikstrom: Fixed to use struct TimeRequest instead of timerequest&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Date Functions =&lt;br /&gt;
&lt;br /&gt;
To ease date-related calculations, the utility library has some functions to convert a date, specified in a ClockData structure, in the number of seconds since 00:00:00 01-Jan-78 and vice versa.&lt;br /&gt;
&lt;br /&gt;
To indicate the date, the ClockData structure (in &amp;amp;lt;utility/date.h&amp;amp;gt;) is used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ClockData&lt;br /&gt;
{&lt;br /&gt;
    UWORD sec;     /* seconds (0 - 59) */&lt;br /&gt;
    UWORD min;     /* minutes (0 - 59) */&lt;br /&gt;
    UWORD hour;    /* hour (0 - 23) */&lt;br /&gt;
    UWORD mday;    /* day of the month (1 - 31) */&lt;br /&gt;
    UWORD month;   /* month of the year (1 - 12) */&lt;br /&gt;
    UWORD year;    /* 1978 - */&lt;br /&gt;
    UWORD wday;    /* day of the week (0 - 6, where 0 is Sunday) */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following functions are available to operate on ClockData:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Utility Library Date Functions&lt;br /&gt;
| Amiga2Date() || Calculate the date from the specified timestamp (in seconds).&lt;br /&gt;
|-&lt;br /&gt;
| CheckDate() || Check the legality of a date.&lt;br /&gt;
|-&lt;br /&gt;
| Date2Amiga() || Calculate the timestamp from the specified date.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Amiga2Date() takes a number of seconds from 01-Jan-78 as argument and fills in the supplied ClockData structure with the date and time.&lt;br /&gt;
&lt;br /&gt;
CheckDate() checks if the supplied ClockData structure is valid, and returns the number of seconds from 01-Jan-78 if it is. Note that this function currently does not take the supplied day of the week in account.&lt;br /&gt;
&lt;br /&gt;
Date2Amiga() takes a ClockData structure as argument and returns the number of seconds since 01-Jan-78. The supplied ClockData structure &#039;&#039;must&#039;&#039; be valid, since no checking is done.&lt;br /&gt;
&lt;br /&gt;
The following example shows various uses of the utility library date functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// a2d.c&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/datetime.h&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/timer.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Note these three libraries are opened by newlib startup code.&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/timer.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct TimerIFace *ITimer = NULL;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct MsgPort *mp = IExec-&amp;gt;AllocSysObject(ASOT_PORT, NULL);&lt;br /&gt;
&lt;br /&gt;
  if (mp != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    struct TimeRequest *tr = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
      ASOIOR_Size, sizeof(struct TimeRequest),&lt;br /&gt;
      ASOIOR_ReplyPort, mp,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (tr != NULL)&lt;br /&gt;
    {&lt;br /&gt;
      if (!IExec-&amp;gt;OpenDevice(&amp;quot;timer.device&amp;quot;, UNIT_VBLANK, (struct IORequest *)tr, 0))&lt;br /&gt;
      {&lt;br /&gt;
        struct Library *TimerBase = (struct Library *)tr-&amp;gt;Request.io_Device;&lt;br /&gt;
        ITimer = (struct TimerIFace*)IExec-&amp;gt;GetInterface(TimerBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
        if (ITimer != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          struct TimeVal tv;&lt;br /&gt;
          ITimer-&amp;gt;GetSysTime(&amp;amp;tv);&lt;br /&gt;
&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;GetSysTime():\t%d %d\n&amp;quot;, tv.Seconds, tv.Microseconds);&lt;br /&gt;
&lt;br /&gt;
          struct ClockData clockdata;&lt;br /&gt;
          IUtility-&amp;gt;Amiga2Date(tv.Seconds, &amp;amp;clockdata);&lt;br /&gt;
&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Amiga2Date():  sec %d min %d hour %d\n&amp;quot;,&lt;br /&gt;
            clockdata.sec, clockdata.min, clockdata.hour);&lt;br /&gt;
&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;               mday %d month %d year %d wday %d\n&amp;quot;,&lt;br /&gt;
            clockdata.mday, clockdata.month, clockdata.year, clockdata.wday);&lt;br /&gt;
&lt;br /&gt;
          int32 seconds = IUtility-&amp;gt;CheckDate(&amp;amp;clockdata);&lt;br /&gt;
&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;CheckDate():\t%ld\n&amp;quot;, seconds);&lt;br /&gt;
&lt;br /&gt;
          seconds = IUtility-&amp;gt;Date2Amiga(&amp;amp;clockdata);&lt;br /&gt;
&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Date2Amiga():\t%ld\n&amp;quot;, seconds);&lt;br /&gt;
&lt;br /&gt;
          IEXec-&amp;gt;DropInterface((struct Interface*)ITimer);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;CloseDevice((struct IORequest *)tr);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, tr);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, mp);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Date Function Reference =&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the utility library functions which pertain to date conversion. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| CheckDate()&lt;br /&gt;
| Check the legality of a date.&lt;br /&gt;
|-&lt;br /&gt;
| Amiga2Date()&lt;br /&gt;
| Calculate the date from a specified timestamp.&lt;br /&gt;
|-&lt;br /&gt;
| Date2Amiga()&lt;br /&gt;
| Calculate the timestamp from a specified date.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Fredrik Wikstrom</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Date_Functions&amp;diff=11113</id>
		<title>Date Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Date_Functions&amp;diff=11113"/>
		<updated>2020-05-17T21:03:55Z</updated>

		<summary type="html">&lt;p&gt;Fredrik Wikstrom: Added missing message port allocation/freeing&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Date Functions =&lt;br /&gt;
&lt;br /&gt;
To ease date-related calculations, the utility library has some functions to convert a date, specified in a ClockData structure, in the number of seconds since 00:00:00 01-Jan-78 and vice versa.&lt;br /&gt;
&lt;br /&gt;
To indicate the date, the ClockData structure (in &amp;amp;lt;utility/date.h&amp;amp;gt;) is used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ClockData&lt;br /&gt;
{&lt;br /&gt;
    UWORD sec;     /* seconds (0 - 59) */&lt;br /&gt;
    UWORD min;     /* minutes (0 - 59) */&lt;br /&gt;
    UWORD hour;    /* hour (0 - 23) */&lt;br /&gt;
    UWORD mday;    /* day of the month (1 - 31) */&lt;br /&gt;
    UWORD month;   /* month of the year (1 - 12) */&lt;br /&gt;
    UWORD year;    /* 1978 - */&lt;br /&gt;
    UWORD wday;    /* day of the week (0 - 6, where 0 is Sunday) */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following functions are available to operate on ClockData:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Utility Library Date Functions&lt;br /&gt;
| Amiga2Date() || Calculate the date from the specified timestamp (in seconds).&lt;br /&gt;
|-&lt;br /&gt;
| CheckDate() || Check the legality of a date.&lt;br /&gt;
|-&lt;br /&gt;
| Date2Amiga() || Calculate the timestamp from the specified date.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Amiga2Date() takes a number of seconds from 01-Jan-78 as argument and fills in the supplied ClockData structure with the date and time.&lt;br /&gt;
&lt;br /&gt;
CheckDate() checks if the supplied ClockData structure is valid, and returns the number of seconds from 01-Jan-78 if it is. Note that this function currently does not take the supplied day of the week in account.&lt;br /&gt;
&lt;br /&gt;
Date2Amiga() takes a ClockData structure as argument and returns the number of seconds since 01-Jan-78. The supplied ClockData structure &#039;&#039;must&#039;&#039; be valid, since no checking is done.&lt;br /&gt;
&lt;br /&gt;
The following example shows various uses of the utility library date functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// a2d.c&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/datetime.h&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/timer.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Note these three libraries are opened by newlib startup code.&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/timer.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct TimerIFace *ITimer = NULL;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct MsgPort *mp = IExec-&amp;gt;AllocSysObject(ASOT_PORT, NULL);&lt;br /&gt;
&lt;br /&gt;
  if (mp != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    struct timerequest *tr = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
      ASOIOR_Size, sizeof(struct TimeRequest),&lt;br /&gt;
      ASOIOR_ReplyPort, mp,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (tr != NULL)&lt;br /&gt;
    {&lt;br /&gt;
      if (!IExec-&amp;gt;OpenDevice(&amp;quot;timer.device&amp;quot;, UNIT_VBLANK, (struct IORequest *)tr, 0))&lt;br /&gt;
      {&lt;br /&gt;
        struct Library *TimerBase = tr-&amp;gt;tr_node.io_Device;&lt;br /&gt;
        ITimer = (struct TimerIFace*)IExec-&amp;gt;GetInterface(TimerBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
        if (ITimer != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          struct TimeVal tv;&lt;br /&gt;
          ITimer-&amp;gt;GetSysTime(&amp;amp;tv);&lt;br /&gt;
&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;GetSysTime():\t%d %d\n&amp;quot;, tv.Seconds, tv.Microseconds);&lt;br /&gt;
&lt;br /&gt;
          struct ClockData clockdata;&lt;br /&gt;
          IUtility-&amp;gt;Amiga2Date(tv.Seconds, &amp;amp;clockdata);&lt;br /&gt;
&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Amiga2Date():  sec %d min %d hour %d\n&amp;quot;,&lt;br /&gt;
            clockdata.sec, clockdata.min, clockdata.hour);&lt;br /&gt;
&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;               mday %d month %d year %d wday %d\n&amp;quot;,&lt;br /&gt;
            clockdata.mday, clockdata.month, clockdata.year, clockdata.wday);&lt;br /&gt;
&lt;br /&gt;
          int32 seconds = IUtility-&amp;gt;CheckDate(&amp;amp;clockdata);&lt;br /&gt;
&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;CheckDate():\t%ld\n&amp;quot;, seconds);&lt;br /&gt;
&lt;br /&gt;
          seconds = IUtility-&amp;gt;Date2Amiga(&amp;amp;clockdata);&lt;br /&gt;
&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Date2Amiga():\t%ld\n&amp;quot;, seconds);&lt;br /&gt;
&lt;br /&gt;
          IEXec-&amp;gt;DropInterface((struct Interface*)ITimer);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;CloseDevice((struct IORequest *)tr);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, tr);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, mp);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Date Function Reference =&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the utility library functions which pertain to date conversion. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| CheckDate()&lt;br /&gt;
| Check the legality of a date.&lt;br /&gt;
|-&lt;br /&gt;
| Amiga2Date()&lt;br /&gt;
| Calculate the date from a specified timestamp.&lt;br /&gt;
|-&lt;br /&gt;
| Date2Amiga()&lt;br /&gt;
| Calculate the timestamp from a specified date.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Fredrik Wikstrom</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=IFFParse_Library&amp;diff=9244</id>
		<title>IFFParse Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=IFFParse_Library&amp;diff=9244"/>
		<updated>2017-10-18T07:24:13Z</updated>

		<summary type="html">&lt;p&gt;Fredrik Wikstrom: Fixed indentation.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== IFFParse Library ==&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;iffparse.library&#039;&#039; was created to help simplify the job of parsing IFF files. Unlike other IFF libraries, iffparse.library is not form-specific. This means that the job of interpreting the structure and contents of the IFF file is up to you. Previous IFF file parsers were either simple but not general, or general but not simple. IFFParse tries to be both simple &#039;&#039;and&#039;&#039; general.&lt;br /&gt;
&lt;br /&gt;
== The Structure of IFF Files ==&lt;br /&gt;
&lt;br /&gt;
Many people have a misconception that IFF means image files. This is not the case. IFF (short for Interchange File Format) is a method of portably storing structured information in machine-readable form. The actual information can be anything, but the manner in which it is stored is very specifically detailed. This specification is the IFF standard.&lt;br /&gt;
&lt;br /&gt;
The IFF standard was originally designed in 1985 by Electronic Arts in conjunction with a committee of developers. The full standard along with file descriptions and sample code is available at [[IFF_Standard|IFF Standard]].&lt;br /&gt;
&lt;br /&gt;
The goal of the IFF standard is to allow customers to move their own data between independently developed software products. The types of data objects to exchange are open-ended and currently include plain and formatted text, raster and structured graphics, fonts, music, sound effects, musical instrument descriptions, and animation. IFF addresses these needs by defining a standard for self-identifying file structures and rules for accessing these files.&lt;br /&gt;
&lt;br /&gt;
=== Chunks: The Building Blocks of IFF ===&lt;br /&gt;
&lt;br /&gt;
IFF files contain various types and amounts of data grouped in data &#039;&#039;chunks&#039;&#039;, each starting with a four-letter ASCII identifier (the chunk ID) followed by a 32-bit length count (the chunk size). The identifier and length count make it possible for IFF readers to skip over chunks that they don’t understand or don’t care about, and extract only the information they need. It may be helpful to think of these chunks as the building blocks of an IFF file.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig33-1.png|frame|center|The Chunk - The Building Block of IFF]]&lt;br /&gt;
&lt;br /&gt;
The ‘CKID’ (chunk ID) characters of a real chunk will be a four letter identifier such as ‘BODY’ or ‘CMAP’, specifying the type and format of data stored in the chunk. Most chunks contain a simple defined grouping of byte, word, and long variables similar to the contents of a C structure. Others such as sound sample and bitmap image body chunks, contain a stream of compressed data.&lt;br /&gt;
&lt;br /&gt;
Chunk writing is fairly straightforward with the one caveat that all chunks must be word-aligned. Therefore an odd-length chunk must be followed by a pad byte of zero (which is not counted in the size of the chunk). When chunks are nested, the enclosing chunk must state the total size of its composite contents (including any pad bytes).&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About Chunk Length|text=Every IFF data chunk begins with a 4-character identifier field followed by a 32-bit size field (these 8 bytes are sometimes referred to as the chunk header). The size field is a count of the rest of the data bytes in the chunk, &#039;&#039;not&#039;&#039; including any pad byte. Hence, the total space occupied by a chunk is given by its size field (rounded to the nearest even number) + 8 bytes for the chunk header}}&lt;br /&gt;
&lt;br /&gt;
=== Composite Data Types ===&lt;br /&gt;
&lt;br /&gt;
Standard IFF files generally contain a variable number of chunks within one of the standard IFF composite data types (which may be thought of as chunks which contain other chunks). The IFF specification defines three basic composite data types, ‘FORM’, ‘CAT ’, and ‘LIST’. A special composite data type, the ‘PROP’, is found only inside a LIST.&lt;br /&gt;
&lt;br /&gt;
Usage of Composite IFF Types&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| FORM || A grouping of chunks which describe one set of data&lt;br /&gt;
|-&lt;br /&gt;
| LIST || A grouping of the same type of FORMs with shared properties in a PROP&lt;br /&gt;
|-&lt;br /&gt;
| PROP || May appear in a LIST to define chunks shared by several FORMs&lt;br /&gt;
|-&lt;br /&gt;
| CAT || A concatenation of related FORMs or LISTs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The special IFF composite data types, like simple chunks, start with a 4-character identifier (such as ‘FORM’), followed by a 32-bit length. But their data begins with a second 4-character identifier that tells you what type of data is in the composite. For example, a FORM ILBM contains chunks describing a bitmap image, and a FORM FTXT contains chunks describing formatted text.&lt;br /&gt;
&lt;br /&gt;
It may help to think of each composite data type as a box containing chunks, the IFF building blocks. The following figure shows a diagram of a typical composite.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig33-2.png|frame|center|The FORM – The Most Common IFF File Type]]&lt;br /&gt;
&lt;br /&gt;
The example FORM in the previous figure is outlined below showing the size of chunks within the FORM. Notice that the size of a composite &#039;&#039;includes&#039;&#039; its second 4-character identifier (shown below as ‘NAME’).&lt;br /&gt;
&lt;br /&gt;
 . FORM 72 NAME  (72 is the size of the FORM starting with the “N” of NAME)&lt;br /&gt;
 . . CKID 22     (then 22 bytes of data, so chunk header + chunk size is 30)&lt;br /&gt;
 . . CKID 10     (then 10 bytes of data, so chunk header + chunk size is 18)&lt;br /&gt;
 . . CKID 12     (then 12 bytes of data, so chunk header + chunk size is 20)&lt;br /&gt;
&lt;br /&gt;
{{Note|text=In the example above, indentation represents the nesting of the chunks within the FORM. Real FORMs and chunks would have different four-character identifiers rather than ‘NAME’ and ‘CKID’.}}&lt;br /&gt;
&lt;br /&gt;
Any odd-length chunks must have a pad byte of zero at the end of the chunk. As shown below, this pad byte is not counted in the size of the chunk but &#039;&#039;is&#039;&#039; counted in the size of any composite types (such as FORM) that contain an odd-length chunk. &#039;&#039;&#039;Warning:&#039;&#039;&#039; some IFF readers and writers do not deal with this properly.&lt;br /&gt;
&lt;br /&gt;
 . FORM 72 NAME  (72 is the size of the FORM starting with the “N” of NAME)&lt;br /&gt;
 . . CKID 21     (then 21 bytes of data, so chunk header + chunk size is 29)&lt;br /&gt;
 . . 0           (pad byte of zero, size 1)&lt;br /&gt;
 . . CKID 10     (then 10 bytes of data, so chunk header + chunk size is 18)&lt;br /&gt;
 . . CKID 12     (then 12 bytes of data, so chunk header + chunk size is 20)&lt;br /&gt;
&lt;br /&gt;
Most IFF files are of the composite type FORM. Generally, a FORM is a simple grouping of chunks that provide information about some data, and the data itself. Although some standard chunks have common usage within different FORMS, the identity and format of most chunks within a FORM are relative to the definition and specification of the FORM. For example, a CRNG chunk in a FORM ILBM may have a totally different format and contents than a chunk named CRNG in a different type of FORM.&lt;br /&gt;
&lt;br /&gt;
One of the most popular IFF FORMs that has been defined is the ILBM standard. This is how most Amiga bitmap imagery is stored. Since this is the most common IFF file, it is used frequently as an example.&lt;br /&gt;
&lt;br /&gt;
Here is the output of a program called “Sift.c” that displays a text description of the contents of an IFF file ([[#IFF Scanner Example|Sift.c]] is listed at the end of this article). The file shown below is a fairly simple ILBM.&lt;br /&gt;
&lt;br /&gt;
 . FORM 11068 ILBM&lt;br /&gt;
 . . BMHD 20&lt;br /&gt;
 . . CAMG 4&lt;br /&gt;
 . . CMAP 12&lt;br /&gt;
 . . BODY 10995&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Computing the Size of a FORM|text=The size of the FORM (11,068 bytes) is equal to the sum of the sizes stated in each chunk contained within the FORM, plus 8 bytes for the overhead of each chunk header (4 bytes for the 4-character chunk ID, and 4 bytes for the 32-bit chunk size), plus 4 bytes for the FORM’s own 4-character ID (‘ILBM’), plus 1 byte more for each pad byte that follow any odd-length chunks.}}&lt;br /&gt;
&lt;br /&gt;
== Parsing an IFF File ==&lt;br /&gt;
&lt;br /&gt;
Chunk reading requires a parser to scan each chunk and dispatch the proper access/conversion procedure. For a simple IFF file, such parsing may be relatively easy, consisting mainly reading in the data of desired chunks, and seeking over unwanted chunks (and the pad byte after odd-length chunks). Interpreting nested chunks is more complex, and requires a method for keeping track of the current context, i.e., the data which is still relevant at any particular depth into the nest. The original IFF specifications compare an IFF file to a C program. Such a metaphor can be useful in understanding the scope of of the chunks in an IFF file.&lt;br /&gt;
&lt;br /&gt;
The IFFParse library addresses general IFF parsing requirements by providing a run-time library which can extract the chunks you want from an IFF file, with the ability to pass control to you when it reaches a chunk that requires special processing such as decompression. IFFParse also understands complex nested IFF formats and will keep track of the context for you.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functions and Structures of IFFParse Library ===&lt;br /&gt;
&lt;br /&gt;
The structures and flags of the IFFParse library are defined in the include files &amp;amp;lt;libraries/iffparse.h&amp;amp;gt; and &amp;amp;lt;libraries/iffparse.i&amp;amp;gt;. IFF files are manipulated through a structure called an IFFHandle. Only some of the fields in the IFFHandle are publicly documented. The rest are managed internally by IFFParse. This handle is passed to all IFFParse functions, and contains the current parse state and position in the file. An IFFHandle is obtained by calling AllocIFF(), and freed through FreeIFF(). This is the only legal way to obtain and dispose of an IFFHandle.&lt;br /&gt;
&lt;br /&gt;
The public portion of if IFFHandle is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Structure associated with an active IFF stream.&lt;br /&gt;
 * &amp;quot;iff_Stream&amp;quot; is a value used by the client&#039;s read/write/seek functions -&lt;br /&gt;
 * it will not be accessed by the library itself and can have any value&lt;br /&gt;
 * (could even be a pointer or a BPTR).&lt;br /&gt;
 */&lt;br /&gt;
struct IFFHandle {&lt;br /&gt;
        ULONG   iff_Stream;&lt;br /&gt;
        ULONG   iff_Flags;&lt;br /&gt;
        LONG    iff_Depth;      /*  Depth of context stack.  */&lt;br /&gt;
        /*  There are private fields hiding here.  */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stream Management ==&lt;br /&gt;
&lt;br /&gt;
A stream is a linear array of bytes that may be accessed sequentially or randomly. DOS files are streams. IFFParse uses Hook structures (defined in &amp;amp;lt;utility/hooks.h&amp;amp;gt;) to implement a general stream management facility. This allows the IFFParse library to read, write, and seek any type of file handle or device by using an application-provided hook function to open, read, write, seek and close the stream.&lt;br /&gt;
&lt;br /&gt;
Built on top of this facility, IFFParse has two internal stream managers: one for unbuffered DOS files (AmigaDOS filehandles), and one for the Clipboard.&lt;br /&gt;
&lt;br /&gt;
=== Initialization ===&lt;br /&gt;
&lt;br /&gt;
As shown above, the IFFHandle structure contains the public field iff_Stream. This is a longword that must be initialized to contain something meaningful to the stream manager. IFFParse never looks at this field itself (at least not directly). Your iff_Stream may be an AmigaDOS filehandle, or an IFFParse ClipboardHandle, or a custom stream of any type if you provide your own custom stream handler (see the section on [[#Custom Stream Handlers|Custom Stream Handlers]]). You must initialize your IFFHandle structure’s iff_Stream to your stream, and then initialize the IFFHandle’s flags and stream hook.&lt;br /&gt;
&lt;br /&gt;
Three sample initializations are shown here. In the case of the internal DOS stream manager, iff_Stream is an AmigaDOS filehandle as returned by Open():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) IDOS-&amp;gt;Open (&amp;quot;filename&amp;quot;, MODE_OLDFILE);&lt;br /&gt;
if(iff-&amp;gt;iff_Stream) IIFFParse-&amp;gt;InitIFFasDOS (iff);  /* use internal DOS stream manager */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the case of the internal Clipboard stream manager, iff_Stream is a pointer to an IFFParse ClipboardHandle structure (the OpenClipboard() call used here is a function of iffparse.library, not the clipboard.device):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard (PRIMARY_CLIP);&lt;br /&gt;
IIFFParse-&amp;gt;InitIFFasClip (iff); /* use internal Clipboard stream manager  */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When using a custom handle such as an fopen() file handle, or a device other than the clipboard, you must provide your own flags and stream handler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) OpenMyCustomStream(&amp;quot;foo&amp;quot;);&lt;br /&gt;
IIFFParse-&amp;gt;InitIFF (iff, IFFF_FSEEK | IFFF_RSEEK, &amp;amp;mystreamhook);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
IFFParse “knows” the seek capabilities of DOS and ClipboardHandle streams, so InitIFFasDOS() and InitIFFasClip() set the flags for you.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=You May Change the Seek Bits in iff_Flags|text=IFFParse sets IFFF_FSEEK | IFFF_RSEEK for DOS files. This is not always true (e.g., pipes). If you know that a DOS file has different seek characteristics, your application may correct the seek bits in iff_Flags after calling InitIFFasDOS().}}&lt;br /&gt;
&lt;br /&gt;
When using InitIFF() to provide a custom handler, you must also provide flags to tell IFFParse the capabilities of your stream. The flags are:&lt;br /&gt;
&lt;br /&gt;
; IFFF_FSEEK&lt;br /&gt;
: This stream has forward-seek capability only.&lt;br /&gt;
&lt;br /&gt;
; IFFF_RSEEK&lt;br /&gt;
: This stream has random-seek capability. IFFF_RSEEK tends to imply IFFF_FSEEK, but it’s best to specify both.&lt;br /&gt;
&lt;br /&gt;
If neither flag is specified, you’re telling IFFParse that it can’t seek through the stream.&lt;br /&gt;
&lt;br /&gt;
After your stream is initialized, call OpenIFF():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;OpenIFF (iff, IFFF_READ);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you establish a read/write mode (by passing IFFF_READ or IFFF_WRITE), you remain in that mode until you call CloseIFF().&lt;br /&gt;
&lt;br /&gt;
=== Termination ===&lt;br /&gt;
&lt;br /&gt;
Termination is simple. Just call CloseIFF(iff). This may be called at any time, and terminates IFFParse’s transaction with the stream. The stream itself is not closed. The IFFHandle may be reused; you may safely call OpenIFF() on it again. You are responsible for closing the streams you opened. A stream used in an IFFHandle must generally remain open until you CloseIFF().&lt;br /&gt;
&lt;br /&gt;
=== Custom Streams ===&lt;br /&gt;
&lt;br /&gt;
A custom stream handler can allow you (and iffparse.library) to use your compiler’s own file I/O functions such as fopen(), fread() and fwrite(), rather than the lower-level AmigaDOS equivalents Open(), Read(), and Write(). A custom stream handler could also be used to read or write IFF files from an Exec device or an unusual handler or file system.&lt;br /&gt;
&lt;br /&gt;
If you install your own stream handler function, iffparse.library will call your function whenever it needs to read, write, or seek on your file. Your stream handler function will then perform these stream actions for iffparse.library. See the “Custom Stream Handlers” section for more information on custom stream handlers.&lt;br /&gt;
&lt;br /&gt;
== Parsing and Writing IFF ==&lt;br /&gt;
&lt;br /&gt;
For information on parsing IFF see [[Parsing_IFF|Parsing IFF]].&lt;br /&gt;
&lt;br /&gt;
For information on writing IFF see [[Writing_IFF|Writing IFF]].&lt;br /&gt;
&lt;br /&gt;
== Context Functions ==&lt;br /&gt;
&lt;br /&gt;
Internally, IFFParse maintains IFF nesting and scoping context via a &#039;&#039;context stack&#039;&#039;. The PushChunk() and PopChunk() functions get their names from this basic idea of the iffparse.library. Direct access to this stack is not allowed. However, many functions are provided to assist in examining and manipulating the context stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About the Context Stack|text=It is probably easier to think of a stack of blocks on a table in front of you when reading this discussion.}}&lt;br /&gt;
&lt;br /&gt;
As the nesting level increases (as would happen when parsing a nested LIST or FORM), the depth of the context stack increases; new elements are added to the top. When these contexts expire, the ContextNodes are deleted and the stack shrinks.&lt;br /&gt;
&lt;br /&gt;
=== Context Nodes ===&lt;br /&gt;
&lt;br /&gt;
The current context is said to be the top element on the stack. Contextual information is stored in a structure called a ContextNode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ContextNode {&lt;br /&gt;
        struct MinNode  cn_Node;&lt;br /&gt;
        LONG            cn_ID;&lt;br /&gt;
        LONG            cn_Type;&lt;br /&gt;
        LONG            cn_Size;        /*  Size of this chunk             */&lt;br /&gt;
        LONG            cn_Scan;        /*  # of bytes read/written so far */&lt;br /&gt;
        /*  There are private fields hiding here.  */&lt;br /&gt;
        };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CurrentChunk() ====&lt;br /&gt;
&lt;br /&gt;
You can obtain a pointer to the current ContextNode through the function CurrentChunk():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
currentnode = IIFFParse-&amp;gt;CurrentChunk (iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ContextNode tells you the type, ID, and size of the currently active chunk. If there is no currently active context, NULL is returned.&lt;br /&gt;
&lt;br /&gt;
==== ParentChunk() ====&lt;br /&gt;
&lt;br /&gt;
To find the parent of a context, you call ParentChunk() on the relevant ContextNode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
parentnode = IIFFParse-&amp;gt;ParentChunk(currentnode);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If there is no parent context, NULL is returned.&lt;br /&gt;
&lt;br /&gt;
=== The Default Context ===&lt;br /&gt;
&lt;br /&gt;
When you first obtain an IFFHandle through AllocIFF(), a hidden default context node is created. You cannot get direct access to this node through CurrentChunk() or ParentChunk(). However, using StoreLocalItem(), you can store information in this context.&lt;br /&gt;
&lt;br /&gt;
=== Context-Specific Data: LocalContextItems ===&lt;br /&gt;
&lt;br /&gt;
ContextNodes can contain application data specific to that context. These data objects are called LocalContextItems. LocalContextItems (LCIs) are a grey-box structure which contain a type, ID and identification field. LCIs are used to store context-sensitive data. The format of this data is application-defined. A ContextNode can contain as many LCIs as you want.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig33-3.png|frame|center|IFFParse Context Stack]]&lt;br /&gt;
&lt;br /&gt;
The previous figure shows the relationship between the IFFHandle, the ContextNodes, and the LCIs. The IFFHandle is the root of the tree, so to speak. ContextNodes “grow” out of the IFFHandle. In turn, LCIs &amp;quot;grow&amp;quot; out of the ContextNodes. What grows out of an LCI is client-defined.&lt;br /&gt;
&lt;br /&gt;
==== AllocLocalItem() ====&lt;br /&gt;
&lt;br /&gt;
To create an LCI, you use the function AllocLocalItem():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
lci = IIFFParse-&amp;gt;AllocLocalItem (type, id, ident, datasize);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If successful, you will be returned a pointer to an LCI having the specified type, ID, and identification values; and with datasize bytes of buffer space for your application to use.&lt;br /&gt;
&lt;br /&gt;
==== LocalItemData() ====&lt;br /&gt;
&lt;br /&gt;
To get a pointer to an LCIs data buffer, you use LocalItemData():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
buf = IIFFParse-&amp;gt;LocalItemData (lci);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may read and write the buffer to your heart’s content; it is yours. You should not, however, write beyond the end of the buffer. The size of the buffer is what you asked for when you called AllocLocalItem().&lt;br /&gt;
&lt;br /&gt;
=== Storing LCIs ===&lt;br /&gt;
&lt;br /&gt;
Once you’ve created and initialized an LCI, you’ll want to attach it to a ContextNode. Though a ContextNode can have many LCIs, a given LCI can be linked to only one ContextNode. Once linked, an LCI cannot be removed from a ContextNode (this may change in the future). Storing an LCI in a ContextNode is done with the functions StoreLocalItem() and StoreItemInContext().&lt;br /&gt;
&lt;br /&gt;
==== StoreLocalItem() ====&lt;br /&gt;
&lt;br /&gt;
The StoreLocalItem() function is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = StoreLocalItem (iff, lci, position);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The position argument determines where the LCI is stored. The possible values are IFFSLI_ROOT, IFFSLI_TOP, and IFFSLI_PROP.&lt;br /&gt;
&lt;br /&gt;
; IFFSLI_ROOT&lt;br /&gt;
: causes StoreLocalItem() to store your LCI in the default ContextNode.&lt;br /&gt;
&lt;br /&gt;
; IFFSLI_TOP&lt;br /&gt;
: gets your LCI stored in the top (current) ContextNode.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=The LCI Ends When the Current Context Ends|text=When the current context expires, your LCI will be deleted by the parser.}}&lt;br /&gt;
&lt;br /&gt;
IFFSLI_PROP causes your LCI to be stored in the topmost context from which a property would apply. This is usually the topmost FORM or LIST chunk. For example, suppose you had a deeply nested ILBM FORM, and you wanted to store the BMHD property in its correct context such that, when the current FORM context expired, the BMHD property would be deleted. IFFSLI_PROP will cause StoreLocalItem() to locate the proper context for such scoping, and store the LCI there. See the section on &amp;quot;Finding the Prop Context&amp;quot; for additional information on the scope of properties.&lt;br /&gt;
&lt;br /&gt;
==== StoreItemInContext() ====&lt;br /&gt;
&lt;br /&gt;
StoreItemInContext() is used when you already have a pointer to the ContextNode to which you want to attach your LCI. It is called like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IIFFParse-&amp;gt;StoreItemInContext (iff, lci, contextnode);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
StoreItemInContext() links your LCI into the specified ContextNode. Then it searches the ContextNode to see if there is another LCI with the same type, ID, and identification values. If so, the old one is deleted.&lt;br /&gt;
&lt;br /&gt;
==== FindLocalItem() ====&lt;br /&gt;
&lt;br /&gt;
After you’ve stored your LCI in a ContextNode, you will no doubt want to be able to find it again later. You do this with the function FindLocalItem(), which is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
lci = IIFFParse-&amp;gt;FindLocalItem (iff, type, id, ident);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FindLocalItem() attempts to locate an LCI having the specified type, ID, and identification values. The search proceeds as follows (refer to the previous figure to understand this better).&lt;br /&gt;
&lt;br /&gt;
FindLocalItem() starts at the top (current) ContextNode and searches all LCIs in that context. If no matching LCIs are found, it proceeds down the context stack to the next ContextNode and searches all its LCIs. The process repeats until it finds the desired LCI (whereupon it returns a pointer to it), or reaches the end without finding anything (where it returns NULL).&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Context Stack Position|text=LCIs higher in the stack will &amp;quot;override&amp;quot; lower LCIs with the same type, ID, and identification field. This is how property scoping is handled. As ContextNodes are popped off the context stack, all its LCIs are deleted as well. See the section on “Freeing LCIs” below for additional information on deleting LCIs.}}&lt;br /&gt;
&lt;br /&gt;
=== Some Interesting Internal Details ===&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=This section details some internal implementation details of iffparse.library which may help you to understand it better. Use of the following information to do “clever” things in an application is forbidden and unsupportable. Don’t even think about it.}}&lt;br /&gt;
&lt;br /&gt;
It turns out that StoredProperties, CollectionItems, and entry and exit handlers are all implemented using LCIs. For example, when you call FindProp(), you are actually calling a front-end to FindLocalItem(). The mysterious identification value (which has heretofore never been discussed) is a value which permits you to differentiate between LCIs having the same type and ID.&lt;br /&gt;
&lt;br /&gt;
For instance, suppose you called PropChunk(), asking it to store an ILBM BMHD. PropChunk() will install an entry handler in the form of an LCI, having type equal to ‘ILBM’, ID equal to ‘BMHD’, and an identification value of IFFLCI_ENTRYHANDLER.&lt;br /&gt;
&lt;br /&gt;
When an ILBM BMHD is encountered, the entry handler is called, and it creates and stores another LCI having type equal to ‘ILBM’, ID equal to ‘BMHD’ and an identification value of IFFLCI_PROP.&lt;br /&gt;
&lt;br /&gt;
Thus, when you call FindProp(), it merely calls FindLocalItem() with your type and ID, and supplies IFFLCI_PROP for the identification value.&lt;br /&gt;
&lt;br /&gt;
Therefore, handlers, StoredProperties, CollectionItems and your own custom LCIs can never be confused with each other, since they all have unique identification values. Since they are all handled (and searched for) in the same way, they all “override” each other in a consistent way. Just as StoredProperties higher in the context stack will be found and returned before identical ones in lower contexts, so will chunk handlers be found and invoked before ones lower on the context stack (recall FindLocalItem()’s search procedure).&lt;br /&gt;
&lt;br /&gt;
This means you can temporarily override a chunk handler by installing an identical handler in a higher context. The handler will persist until the context in which it is stored expires, after which, the original one regains scope.&lt;br /&gt;
&lt;br /&gt;
== Error Handling ==&lt;br /&gt;
&lt;br /&gt;
If at any time during reading or writing you encounter an error, the IFFHandle is left in an undefined state. Upon detection of an error, you should perform an abort sequence and CloseIFF() the IFFHandle. Once CloseIFF() has been called, the IFFHandle is restored to normalcy and may be reused.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
&lt;br /&gt;
This section discusses customizing of IFFParse data handling. For applications with special needs, IFFParse supports both custom stream handlers and custom chunk handlers.&lt;br /&gt;
&lt;br /&gt;
=== Custom Stream Handlers ===&lt;br /&gt;
&lt;br /&gt;
As discussed earlier, IFFParse contains built-in stream handlers for AmigaDOS file handles as returned by Open(), and for the clipboard.device. If you are using AmigaDOS filehandles or the clipboard.device, you need not supply a custom stream handler.&lt;br /&gt;
&lt;br /&gt;
If you wish to use your compiler’s own file I/O functions (such as fread() ) or need to read or write to an unusual handler or Exec device, you must provide a custom stream handler for your IFFHandle. Your custom stream handler will be called to perform all reads, writes, and seeks on your custom stream. The allows you to use compiler file I/O functions, Exec device commands, or any other method to perform the requested stream operations.&lt;br /&gt;
&lt;br /&gt;
If you are implementing your own custom stream handler, you will need to know the mechanics of hook call-backs, and how to interpret the parameters. An IFFParse custom stream handler is simply a function in your code that follows hook function conventions (Hook functions are also known as callback functions. See [[Utility_Library|Utility Library]] for more details).&lt;br /&gt;
&lt;br /&gt;
==== Installing a Custom Stream Handler ====&lt;br /&gt;
&lt;br /&gt;
To initialize your IFFHandle to point to your custom stream and stream handler, you must open your stream and place a pointer to the stream in your IFFHandle’s iff_Stream field. This pointer could be the return from fopen(), or an Exec device I/O request, or any other type of pointer which will provide your custom stream handler with a way to manage your stream. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) fopen(&amp;quot;foo&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, you must install your custom handler for this stream, and also tell IFFParse the seek capabilities of your stream. To install your custom stream handler, you must first set up a Hook structure (&amp;amp;lt;utility/hooks.h&amp;amp;gt;) to point to your stream handling function. Then use InitIFF() to install your stream handler and also tell IFFParse the seek capabilities of your stream. There is “some assembly required”.&lt;br /&gt;
&lt;br /&gt;
For an IFFParse custom stream hook the function prototype looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 mystreamhandler(struct Hook *hook, struct IFFHandle *iff, struct IFFStreamCmd *actionpkt);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A return of 0 indicates success. A non-zero return indicates an error.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
static int32 mystreamhandler&lt;br /&gt;
(&lt;br /&gt;
    struct Hook         *hook,&lt;br /&gt;
    struct IFFHandle    *iff,&lt;br /&gt;
    struct IFFStreamCmd *actionpkt&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
/*&lt;br /&gt;
 * Code to handle the stream commands - see end this section&lt;br /&gt;
 * for a complete example custom stream handler function.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Initialization of Hook structure for registerized handler function */&lt;br /&gt;
struct Hook mystreamhook&lt;br /&gt;
{&lt;br /&gt;
  { NULL },&lt;br /&gt;
  (HOOKFUNC) mystreamhandler,  /* h_Entry, registerized function entry */&lt;br /&gt;
  NULL,&lt;br /&gt;
  NULL&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Open custom stream and InitIFF to custom stream handler */&lt;br /&gt;
if ( iff-&amp;gt;iff_Stream = (ULONG) myopen(&amp;quot;foo&amp;quot;) )&lt;br /&gt;
        {&lt;br /&gt;
        IIFFParse-&amp;gt;InitIFF (iff, IFFF_FSEEK | IFFF_RSEEK, &amp;amp;mystreamhook);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Inside a Custom Stream Handler ====&lt;br /&gt;
&lt;br /&gt;
When the library calls your stream handler, you’ll be passed a pointer to the Hook structure (hook in the example used here), a pointer to the IFFHandle (iff), and a pointer to an IFFStreamCmd structure (actionpkt). Your stream pointer may be found in iff-&amp;amp;gt;iff_Stream where you previously stored it. The IFFStreamCmd (actionpkt) will describe the action that IFFParse needs you to perform on your stream:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Custom stream handler is passed struct IFFStreamCmd *actionpkt */&lt;br /&gt;
struct IFFStreamCmd {&lt;br /&gt;
        LONG    sc_Command;     /*  Operation to be performed (IFFCMD_) */&lt;br /&gt;
        APTR    sc_Buf;         /*  Pointer to data buffer              */&lt;br /&gt;
        LONG    sc_NBytes;      /*  Number of bytes to be affected      */&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
/* Possible call-back command values.  (Using 0 as the value for IFFCMD_INIT&lt;br /&gt;
 * was, in retrospect, probably a bad idea.)&lt;br /&gt;
 */&lt;br /&gt;
#define IFFCMD_INIT     0       /*  Prepare the stream for a session    */&lt;br /&gt;
#define IFFCMD_CLEANUP  1       /*  Terminate stream session            */&lt;br /&gt;
#define IFFCMD_READ     2       /*  Read bytes from stream              */&lt;br /&gt;
#define IFFCMD_WRITE    3       /*  Write bytes to stream               */&lt;br /&gt;
#define IFFCMD_SEEK     4       /*  Seek on stream                      */&lt;br /&gt;
#define IFFCMD_ENTRY    5       /*  You just entered a new context      */&lt;br /&gt;
#define IFFCMD_EXIT     6       /*  You&#039;re about to leave a context     */&lt;br /&gt;
#define IFFCMD_PURGELCI 7       /*  Purge a LocalContextItem            */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Your custom stream handler should perform the requested action on your custom stream, and then return 0 for success or an IFFParse error if an error occurred. The following code demonstrates a sample stream handler for a stream which was opened with a compiler’s fopen() buffered file I/O function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
static int32 mystreamhandler ( struct Hook *hook,&lt;br /&gt;
                              struct IFFHandle *iff,&lt;br /&gt;
                              struct IFFStreamCmd *actionpkt )&lt;br /&gt;
{&lt;br /&gt;
        FILE   *stream;&lt;br /&gt;
        int32  nbytes, error;&lt;br /&gt;
        uint8  *buf;&lt;br /&gt;
&lt;br /&gt;
        stream  = (FILE *) iff-&amp;gt;iff_Stream;     /* get your stream pointer */&lt;br /&gt;
        nbytes  = actionpkt-&amp;gt;sc_NBytes;         /* length for command */&lt;br /&gt;
        buf     = (uint8 *) actionpkt-&amp;gt;sc_Buf;  /* buffer for the command */&lt;br /&gt;
&lt;br /&gt;
        /* Now perform the action specified by the actionpkt-&amp;amp;gt;sc_Command */&lt;br /&gt;
&lt;br /&gt;
        switch (actionpkt-&amp;gt;sc_Command) {&lt;br /&gt;
        case IFFCMD_READ:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_READ means read sc_NBytes from the stream and place&lt;br /&gt;
                 * it in the memory pointed to by sc_Buf.  Be aware that&lt;br /&gt;
                 * sc_NBytes may be larger than can be contained in an int.&lt;br /&gt;
                 * This is important if you plan on recompiling this for&lt;br /&gt;
                 * 16-bit ints, since fread() takes int arguments.&lt;br /&gt;
                 *&lt;br /&gt;
                 * Any error code returned will be remapped by IFFParse into&lt;br /&gt;
                 * IFFERR_READ.&lt;br /&gt;
                 */&lt;br /&gt;
                error = (fread (buf, 1, nbytes, stream) != nbytes);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
        case IFFCMD_WRITE:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_WRITE is analogous to IFFCMD_READ.&lt;br /&gt;
                 *&lt;br /&gt;
                 * Any error code returned will be remapped by IFFParse into&lt;br /&gt;
                 * IFFERR_WRITE.&lt;br /&gt;
                 */&lt;br /&gt;
                error = (fwrite (buf, 1, nbytes, stream) != nbytes);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
        case IFFCMD_SEEK:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_SEEK asks that you performs a seek relative to the&lt;br /&gt;
                 * current position.  sc_NBytes is a signed number,&lt;br /&gt;
                 * indicating seek direction (positive for forward, negative&lt;br /&gt;
                 * for backward).  sc_Buf has no meaning here.&lt;br /&gt;
                 *&lt;br /&gt;
                 * Any error code returned will be remapped by IFFParse into&lt;br /&gt;
                 * IFFERR_SEEK.&lt;br /&gt;
                 */&lt;br /&gt;
                error = (fseek (stream, nbytes, 1) == -1);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
        case IFFCMD_INIT:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_INIT means to prepare your stream for reading.&lt;br /&gt;
                 * This is used for certain streams that can&#039;t be read&lt;br /&gt;
                 * immediately upon opening, and need further preparation.&lt;br /&gt;
                 * This operation is allowed to fail;  the error code placed&lt;br /&gt;
                 * in D0 will be returned directly to the client.&lt;br /&gt;
                 *&lt;br /&gt;
                 * An example of such a stream is the clipboard.  The&lt;br /&gt;
                 * clipboard.device requires you to set the io_ClipID and&lt;br /&gt;
                 * io_Offset to zero before starting a read.  You would&lt;br /&gt;
                 * perform such a sequence here.  (Stdio files need no such&lt;br /&gt;
                 * preparation, so we simply return zero for success.)&lt;br /&gt;
                 */&lt;br /&gt;
        case IFFCMD_CLEANUP:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_CLEANUP means to terminate the transaction with&lt;br /&gt;
                 * the associated stream.  This is used for streams that&lt;br /&gt;
                 * can&#039;t simply be closed.  This operation is not allowed to&lt;br /&gt;
                 * fail;  any error returned will be ignored.&lt;br /&gt;
                 *&lt;br /&gt;
                 * An example of such a stream is (surprise!) the clipboard.&lt;br /&gt;
                 * It requires you to explicitly end reads by CMD_READing&lt;br /&gt;
                 * past the end of a clip, and end writes by sending a&lt;br /&gt;
                 * CMD_UPDATE.  You would perform such a sequence here.&lt;br /&gt;
                 * (Again, stdio needs no such sequence.)&lt;br /&gt;
                 */&lt;br /&gt;
                error = 0;&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        return (error);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Custom Chunk Handlers ===&lt;br /&gt;
&lt;br /&gt;
Like custom stream handlers, custom chunk handlers are implemented using Hook structures. See the previous section for details on how a handler function may be interfaced using a Hook structure.&lt;br /&gt;
&lt;br /&gt;
There are two types of chunk handlers: entry handlers and exit handlers. Entry handlers are invoked just after the parser enters the chunk; the stream will be positioned to read the first byte in the chunk. (If the chunk is a FORM, LIST, CAT, or PROP, the longword type will be read by the parser; the stream will be positioned to read the byte immediately following the type.) Exit handlers are invoked just before the parser leaves the chunk; the stream is not positioned predictably within the chunk.&lt;br /&gt;
&lt;br /&gt;
==== Installing a Custom Chunk Handler ====&lt;br /&gt;
&lt;br /&gt;
To install an entry handler, you call EntryHandler() in the following manner:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;EntryHandler (iff, type, id, position, hookptr, object);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An exit handler is installed by saying:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;ExitHandler (iff, type, id, position, hookptr, object);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In both cases, a handler is installed for chunks having the specified type and id. The position argument specifies in what context to install the handler, and is identical to the position argument used by StoreLocalItem(). The hookptr argument given above is a pointer to your Hook structure.&lt;br /&gt;
&lt;br /&gt;
==== Inside a Custom Chunk Handler ====&lt;br /&gt;
&lt;br /&gt;
The mechanics of receiving parameters through the Hook are covered in the “Custom Stream Handlers” section, and are not duplicated here. Refer to the EntryHandler() and ExitHandler() Autodocs for the contents of the registers upon entry.&lt;br /&gt;
&lt;br /&gt;
Once inside your handler, you can call nearly all functions in the iffparse.library, however, you should avoid calling ParseIFF() from within chunk handlers.&lt;br /&gt;
&lt;br /&gt;
Your handler runs in the same environment as whoever called ParseIFF(). The propagation sequence is:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;mainline&#039;&#039;&#039; --&#039;&#039;calls&#039;&#039;--&amp;gt; &#039;&#039;&#039;ParseIFF()&#039;&#039;&#039; --&#039;&#039;calls&#039;&#039;--&amp;gt; &#039;&#039;&#039;your_handler()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Thus, your handler runs on your mainline’s stack, and can call any OS functions the mainline code can. (Your handler will have to set the global base pointer if your code uses base-relative addressing.)&lt;br /&gt;
&lt;br /&gt;
The return code will affect the parser in a number of ways:&lt;br /&gt;
&lt;br /&gt;
* If you return zero (a normal, uneventful return), ParseIFF() will continue normally. If you return the value IFFERR_RETURN2CLIENT, ParseIFF() will stop and return the value zero to the mainline code.&lt;br /&gt;
&lt;br /&gt;
* If you return any other value, ParseIFF() will stop and return that value to the mainline code. This is how you should return error conditions to the client code.&lt;br /&gt;
&lt;br /&gt;
==== The Object Parameter ====&lt;br /&gt;
&lt;br /&gt;
The object parameter supplied to EntryHandler() and ExitHandler() is a pointer which will be passed to your handler when invoked. This pointer can be anything; the parser doesn’t use it directly. As an example, you might pass the pointer to the IFFHandle. This way, when your handler is called, you’ll be able to easily perform operations on the IFFHandle within your handler. Such code might appear as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = EntryHandler (iff, ID_ILBM, ID_BMHD, IFFSLI_ROOT, hook, iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And your handler would be declared as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 MyHandler (struct Hook      *hook,&lt;br /&gt;
                 int32            *cmd,&lt;br /&gt;
                 struct IFFHandle *iff)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From within your handler, you could then call CurrentChunk(), ReadChunkBytes(), or nearly any other operation on the IFFHandle. Please refer to the EntryHandler() and ExitHandler() Autodocs for additional information on the use of chunk handlers.&lt;br /&gt;
&lt;br /&gt;
=== Finding the Prop Context ===&lt;br /&gt;
&lt;br /&gt;
Earlier it was mentioned that supplying a position value of IFFSLI_PROP to StoreLocalItem() would store it in the topmost property scope. FindPropContext() is the routine that finds that topmost context.&lt;br /&gt;
&lt;br /&gt;
Property chunks (such as the BMHD, CMAP, and others) have dominion over the FORM that contains them; they are said to be “in scope” and their definition persists until the FORM’s context ends. Thus, a property chunk has a scoping level equal to the FORM that contains it; when the FORM ends, the property dies with it.&lt;br /&gt;
&lt;br /&gt;
Consider a more complicated example. Suppose you have a LIST with a PROP in it. PROPs are the global variables of LISTs; thus a property chunk declared in a PROP will persist until the LIST’s context ends.&lt;br /&gt;
&lt;br /&gt;
This is what FindPropContext() is looking for; a context level in which a property chunk may be installed.&lt;br /&gt;
&lt;br /&gt;
FindPropContext() starts at the parent of the current context (second from the top of the context stack) and starts searching downward, looking for the first FORM or LIST context it finds. If it finds one, it returns a pointer to that ContextNode. If it can’t find a suitable context level, it returns NULL.&lt;br /&gt;
&lt;br /&gt;
FindPropContext() is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ContextNode *cn = IFFParse-&amp;gt;FindPropContext (iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Freeing LCIs ===&lt;br /&gt;
&lt;br /&gt;
Ordinarily, the parser will automatically delete LCIs you have allocated and installed. However, you may have a case where simply FreeVec()ing your LCI is not enough; you may need to free some ancillary memory, or decrement a counter, or send a signal, or something. This is where SetLocalItemPurge() comes in. It is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IFFParse-&amp;gt;SetLocalItemPurge (lci, hookptr);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the parser is ready to delete your LCI, your purge handler code will be called through the Hook you supplied. You can then perform all your necessary operations. One of these operations should be to free the LCI itself. This is done with FreeLocalItem():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IIFFParse-&amp;gt;FreeLocalItem (lci);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This deallocates the memory used to store the LCI and the client buffer allocated with it. FreeLocalItem() is only called as part of a custom purge handler.&lt;br /&gt;
&lt;br /&gt;
As with custom chunk handlers, your purge handler executes in the same environment as the mainline code that called ParseIFF(). It is recommended that you keep purge handlers short and to the point; super clever stuff should be reserved for custom chunk handlers, or for the client’s mainline code. Custom purge handlers must &#039;&#039;always&#039;&#039; work; failures will be ignored.&lt;br /&gt;
&lt;br /&gt;
== IFF FORM Specifications ==&lt;br /&gt;
&lt;br /&gt;
The specifications for Amiga IFF formats are maintained by the AmigaOS Development Team. The latest specifications are published in the [[IFF_Standard|IFF Standard]]. Updates of the IFF Manual, selected new FORMs and changes to existing FORMs are documented on this wiki.&lt;br /&gt;
&lt;br /&gt;
Some of the most commonly used IFF FORMs are the four that were originally specified in the EA IFF-85 standard:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| ILBM || Bitmap images and palettes&lt;br /&gt;
|-&lt;br /&gt;
| FTXT || Simple formatted text&lt;br /&gt;
|-&lt;br /&gt;
| SMUS || Simple musical scores&lt;br /&gt;
|-&lt;br /&gt;
| 8SVX || 8-bit sound samples&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Of these four, ILBM is the most commonly encountered FORM, and FTXT is becoming increasingly important since the &#039;&#039;conclip&#039;&#039; command passes clipped console text through the clipboard as FTXT. All data clipped to the clipboard must be in an IFF format.&lt;br /&gt;
&lt;br /&gt;
This section will provide a brief summary of the ILBM and FTXT FORMs and their most used common chunks. Please consult the EA-IFF specifications for additional information.&lt;br /&gt;
&lt;br /&gt;
=== FORM ILBM ===&lt;br /&gt;
&lt;br /&gt;
The IFF file format for graphic images on the Amiga is called FORM ILBM (InterLeaved BitMap). It follows a standard parsable IFF format.&lt;br /&gt;
&lt;br /&gt;
==== ILBM.BMHD BitMapHeader Chunk ====&lt;br /&gt;
&lt;br /&gt;
The most important chunk in a FORM ILBM is the BMHD (BitMapHeader) chunk which describes the size and compression of the image:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
typedef UBYTE Masking;  /* Choice of masking technique - Usually 0. */&lt;br /&gt;
#define mskNone                 0&lt;br /&gt;
#define mskHasMask              1&lt;br /&gt;
#define mskHasTransparentColor  2&lt;br /&gt;
#define mskLasso                3&lt;br /&gt;
&lt;br /&gt;
/* Compression algorithm applied to the rows of all source&lt;br /&gt;
 * and mask planes. &amp;amp;quot;cmpByteRun1&amp;amp;quot; is byte run encoding.&lt;br /&gt;
 * Do not compress across rows!  Compression is usually 1.&lt;br /&gt;
 */&lt;br /&gt;
typedef UBYTE Compression;&lt;br /&gt;
#define cmpNone                 0&lt;br /&gt;
#define cmpByteRun1             1&lt;br /&gt;
&lt;br /&gt;
/* The BitMapHeader structure expressed as a C structure */&lt;br /&gt;
typedef struct {&lt;br /&gt;
        UWORD w, h;             /* raster width &amp;amp;amp; height in pixels      */&lt;br /&gt;
        WORD  x, y;             /* pixel position for this image        */&lt;br /&gt;
        UBYTE nPlanes;          /* # bitplanes (without mask, if any)   */&lt;br /&gt;
        Masking     masking;    /* One of the values above.  Usually 0  */&lt;br /&gt;
        Compression compression;/* One of the values above.  Usually 1  */&lt;br /&gt;
        UBYTE reserved1;        /* reserved; ignore on read, write as 0 */&lt;br /&gt;
        UWORD transparentColor; /* transparent color number. Usually 0  */&lt;br /&gt;
        UBYTE xAspect, yAspect; /* pixel aspect, a ratio width : height */&lt;br /&gt;
        WORD  pageWidth, pageHeight;    /* source &amp;amp;quot;page&amp;amp;quot; size in pixels */&lt;br /&gt;
} BitMapHeader;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=This hex dump is shown only to help the reader understand how IFF chunks appear in a file. You cannot ever depend on any particular ILBM chunk being at any particular offset into the file. IFF files are composed, in their simplest form, of chunks within a FORM. Each chunk starts with a 4-letter chunkID, followed by a 32-bit length of the rest of the chunk. You must &#039;&#039;parse&#039;&#039; IFF files, skipping past unneeded or unknown chunks by seeking their length (+1 if odd length) to the next 4-letter chunk ID. In a real ILBM file, you are likely to encounter additional optional chunks. See the IFF Specification listed in [[IFF_Standard|IFF Standard]] for additional information on such chunks.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0000: 464F524D 00016418 494C424D 424D4844    FORM..d.ILBMBMHD&lt;br /&gt;
0010: 00000014 01400190 00000000 06000100    .....@..........&lt;br /&gt;
0020: 00000A0B 01400190 43414D47 00000004    .....@..CAMG....&lt;br /&gt;
0030: 00000804 434D4150 00000030 001000E0    ....CMAP...0....&lt;br /&gt;
0040: E0E00000 20000050 30303050 50500030    .... ..P000PPP.0&lt;br /&gt;
0050: 90805040 70707010 60E02060 E06080D0    ..P@ppp.`. `.`..&lt;br /&gt;
0060: A0A0A0A0 90E0C0C0 C0D0A0E0 424F4459    ............BODY&lt;br /&gt;
0070: 000163AC F8000F80 148A5544 2ABDEFFF    ..c.......UD*...  etc.&lt;br /&gt;
&lt;br /&gt;
Interpretation:&lt;br /&gt;
&lt;br /&gt;
      &#039;F O R M&#039; length  &#039;I L B M&#039;&#039;B M H D&#039;&amp;amp;lt;-start of BitMapHeader chunk&lt;br /&gt;
0000: 464F524D 00016418 494C424D 424D4844    FORM..d.ILBMBMHD&lt;br /&gt;
&lt;br /&gt;
       length  WideHigh XorgYorg PlMkCoRe &amp;amp;lt;- Planes Mask Compression Reserved&lt;br /&gt;
0010: 00000014 01400190 00000000 06000100    .....@..........&lt;br /&gt;
&lt;br /&gt;
      TranAspt PagwPagh &#039;C A M G&#039; length  &amp;amp;lt;- start of C-AMiGa View modes chunk&lt;br /&gt;
0020: 00000A0B 01400190 43414D47 00000004    .....@..CAMG....&lt;br /&gt;
&lt;br /&gt;
dir include:&lt;br /&gt;
      ViewMode &#039;C M A P&#039; length   R g b R &amp;amp;lt;- ViewMode 800=HAM | 4=LACE&lt;br /&gt;
0030: 00000804 434D4150 00000030 001000E0    ....CMAP...0....&lt;br /&gt;
&lt;br /&gt;
       g b R g  b R g b  R g b R  g b R g &amp;amp;lt;- Rgb&#039;s are for reg0 thru regN&lt;br /&gt;
0040: E0E00000 20000050 30303050 50500030    .... ..P000PPP.0&lt;br /&gt;
&lt;br /&gt;
       b R g b  R g b R  g b R g  b R g b&lt;br /&gt;
0050: 90805040 70707010 60E02060 E06080D0    ..P@ppp.`. `.`..&lt;br /&gt;
&lt;br /&gt;
       R g b R  g b R g  b R g b &#039;B O D Y&#039;&lt;br /&gt;
0060: A0A0A0A0 90E0C0C0 C0D0A000 424F4459    ............BODY&lt;br /&gt;
&lt;br /&gt;
       length   start of body data        &amp;amp;lt;- Compacted (Compression=1 above)&lt;br /&gt;
0070: 000163AC F8000F80 148A5544 2ABDEFFF    ..c.......UD*...&lt;br /&gt;
0080: FFBFF800 0F7FF7FC FF04F85A 77AD5DFE    ...........Zw.].  etc.&lt;br /&gt;
&lt;br /&gt;
Simple CAMG ViewModes:  HIRES=0x8000  LACE=0x4  HAM=0x800  HALFBRITE=0x80&lt;br /&gt;
&lt;br /&gt;
( ILBMs may contain a LONGWORD ViewPort ModeID in CAMG )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Interpreting ILBMs ====&lt;br /&gt;
&lt;br /&gt;
ILBM is a fairly simple IFF FORM. All you really need to deal with to extract the image are the following chunks:&lt;br /&gt;
&lt;br /&gt;
Information about the size, depth, compaction method (see interpreted hex dump above).&lt;br /&gt;
&lt;br /&gt;
Optional Amiga ViewModes chunk. Most HAM and HALFBRITE ILBMs should have this chunk. If no CAMG chunk is present, and image is 6 planes deep, assume HAM and you’ll probably be right. Some Amiga ViewModes flags are HIRES=0x8000, LACE=0x4, HAM=0x800, HALFBRITE=0x80. 2.0 ILBM writers should write a full 32-bit mode ID in the CAMG. See the IFF Manual for more information on writing and interpreting 32-bit CAMG values.&lt;br /&gt;
&lt;br /&gt;
RGB values for color registers 0 to N. Previously, 4-bit RGB components each left justified in a byte. These should now be stored as a full 8-bit RGB values by duplicating 4-bit values in the high and low nibble of the byte.&lt;br /&gt;
&lt;br /&gt;
The pixel data, stored in an interleaved fashion as follows (each line individually compacted if BMHD Compression = 1):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
plane 0 scan line 0&lt;br /&gt;
plane 1 scan line 0&lt;br /&gt;
plane 2 scan line 0&lt;br /&gt;
...&lt;br /&gt;
plane n scan line 0&lt;br /&gt;
plane 0 scan line 1&lt;br /&gt;
plane 1 scan line 1&lt;br /&gt;
etc.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also watch for AUTH Author chunks and (c) copyright chunks and preserve any copyright information if you rewrite the ILBM.&lt;br /&gt;
&lt;br /&gt;
==== ILBM BODY Compression ====&lt;br /&gt;
&lt;br /&gt;
The BODY contains pixel data for the image. Width, Height, and depth (Planes) is specified in the BMHD.&lt;br /&gt;
&lt;br /&gt;
If the BMHD Compression byte is 0, then the scan line data is not compressed. If Compression=1, then each scan line is individually compressed as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;while (not produced the desired number of bytes)&lt;br /&gt;
&lt;br /&gt;
    /* get a byte, call it N */&lt;br /&gt;
&lt;br /&gt;
    if (N &amp;amp;gt;= 0 &amp;amp;amp;&amp;amp;amp; N &amp;amp;lt;= 127)&lt;br /&gt;
        /* copy the next N+1 bytes literally */&lt;br /&gt;
&lt;br /&gt;
    if (N &amp;amp;gt;= -127 &amp;amp;amp;&amp;amp;amp; N &amp;amp;lt;= -1)&lt;br /&gt;
        /* repeat the next byte N+1 times */&lt;br /&gt;
&lt;br /&gt;
    if (N == -128)&lt;br /&gt;
        /* skip it, presumably it&#039;s padding */&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== Interpreting the Scan Line Data ====&lt;br /&gt;
&lt;br /&gt;
If the ILBM is not HAM or HALFBRITE, then after parsing and uncompacting if necessary, you will have N planes of pixel data. Color register used for each pixel is specified by looking at each pixel thru the planes. For instance, if you have 5 planes, and the bit for a particular pixel is set in planes 0 and 3:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PLANE     4 3 2 1 0&lt;br /&gt;
PIXEL     0 1 0 0 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
then that pixel uses color register binary 01001 = 9.&lt;br /&gt;
&lt;br /&gt;
The RGB value for each color register is stored in the CMAP chunk of the ILBM, starting with register 0, with each register’s RGB value stored as one byte of R, one byte G, and one byte of B, with each component left justified in the byte. (i.e. Amiga R, G, and B components are each stored in the high nibble of a byte)&lt;br /&gt;
&lt;br /&gt;
But, if the picture is HAM or HALFBRITE, it is interpreted differently. Hopefully, if the picture is HAM or HALFBRITE, the package that saved it properly saved a CAMG chunk (look at a hex dump of your file with ASCII interpretation - you will see the chunks - they all start with a 4-ASCII-char chunk ID). If the picture is 6 planes deep and has no CAMG chunk, it is probably HAM. If you see a CAMG chunk, the ‘CAMG’ is followed by the 32-bit chunk length, and then the 32-bit Amiga view mode flags.&lt;br /&gt;
&lt;br /&gt;
HAM pictures will have the 0x800 bit set in CAMG chunk ViewModes. HALFBRITE pictures will have the 0x80 bit set. See the graphics library articles for more information on HAM and HALFBRITE modes.&lt;br /&gt;
&lt;br /&gt;
==== Other ILBM Notes ====&lt;br /&gt;
&lt;br /&gt;
Amiga ILBMs images must be stored as an even number of bytes in width. However, the ILBM BMHD field w (width) should describe the actual image width, not the rounded up width as stored.&lt;br /&gt;
&lt;br /&gt;
ILBMs created with Electronic Arts IBM or Amiga &#039;&#039;Deluxe Paint II&#039;&#039; packages are compatible (though you may have to use a ‘.lbm’ filename extension on an IBM). The ILBM graphic files may be transferred between the machines (or between the Amiga and IBM sides your Amiga if you have a CBM Bridgeboard card installed) and loaded into either package.&lt;br /&gt;
&lt;br /&gt;
=== FORM FTXT ===&lt;br /&gt;
&lt;br /&gt;
The FTXT (Formatted TeXT) form is the standard format for sharing text on the Amiga. The console device clip and paste functions, in conjunction with the startup-sequence &#039;&#039;conclip&#039;&#039; command, pass clipped console text through the clipboard as FTXT.&lt;br /&gt;
&lt;br /&gt;
By supporting reading and writing of clipboard device FTXT, your application will allow users to cut and paste text between your application, other applications, and Amiga Shell windows.&lt;br /&gt;
&lt;br /&gt;
The FTXT form is very simple. Generally, for clip and paste operations, you will only be concerned with the CHRS (character string) chunks of an FTXT. There may be one or more CHRS chunks, each of which contains a non-NULL-terminated string of ASCII characters whose length is equal to the length (StoredProperty.sp_Size) of the chunk.&lt;br /&gt;
&lt;br /&gt;
Be aware that if you CollectionChunk() the CHRS chunks of an FTXT, the list accumulated by IFFParse will be in reverse order from the chunk order in the file. See the ClipFTXT.c example at the end of this article for a simple example of reading (and optionally writing) FTXT to and from the clipboard. See also the [[Clipboard_Device|Clipboard Device]] and [[Console_Device|Console Device]] for more information on console clip and paste.&lt;br /&gt;
&lt;br /&gt;
== IFFParse Examples ==&lt;br /&gt;
&lt;br /&gt;
Two examples follow: “ClipFTXT.c” and “Sift.c”. These are simple examples that demonstrate the use of the IFFParse library. More complex examples for displaying and saving ILBMs may be found in the [[IFF_Standard|IFF Standard]] section.&lt;br /&gt;
&lt;br /&gt;
=== Clipboard FTXT Example ===&lt;br /&gt;
&lt;br /&gt;
ClipFTXT.c demonstrates reading (and optional writing) of clipboard device FTXT. This example may be used as the basis for supporting console pastes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 *&lt;br /&gt;
 * clipftxt.c:   Writes ASCII text to clipboard unit as FTXT&lt;br /&gt;
 *               (All clipboard data must be IFF)&lt;br /&gt;
 *&lt;br /&gt;
 * Usage: clipftxt unitnumber&lt;br /&gt;
 *&lt;br /&gt;
 * To convert to an example of reading only, comment out #define WRITEREAD&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Causes example to write FTXT first, then read it back&lt;br /&gt;
 * Comment out to create a reader only&lt;br /&gt;
 */&lt;br /&gt;
#define WRITEREAD&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define MINARGS 2&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR usage = &amp;quot;Usage: clipftxt unitnumber (use zero for primary unit)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Text error messages for possible IFFERR_#? returns from various&lt;br /&gt;
 * IFF routines.  To get the index into this array, take your IFFERR code,&lt;br /&gt;
 * negate it, and subtract one.&lt;br /&gt;
 *  idx = -error - 1;&lt;br /&gt;
 */&lt;br /&gt;
char *errormsgs[] = {&lt;br /&gt;
     &amp;quot;End of file (not an error).&amp;quot;,&lt;br /&gt;
     &amp;quot;End of context (not an error).&amp;quot;,&lt;br /&gt;
     &amp;quot;No lexical scope.&amp;quot;,&lt;br /&gt;
     &amp;quot;Insufficient memory.&amp;quot;,&lt;br /&gt;
     &amp;quot;Stream read error.&amp;quot;,&lt;br /&gt;
     &amp;quot;Stream write error.&amp;quot;,&lt;br /&gt;
     &amp;quot;Stream seek error.&amp;quot;,&lt;br /&gt;
     &amp;quot;File is corrupt.&amp;quot;,&lt;br /&gt;
     &amp;quot;IFF syntax error.&amp;quot;,&lt;br /&gt;
     &amp;quot;Not an IFF file.&amp;quot;,&lt;br /&gt;
     &amp;quot;Required call-back hook missing.&amp;quot;,&lt;br /&gt;
     &amp;quot;Return to client.  You should never see this.&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define RBUFSZ 512&lt;br /&gt;
&lt;br /&gt;
#define  ID_FTXT        MAKE_ID(&#039;F&#039;,&#039;T&#039;,&#039;X&#039;,&#039;T&#039;)&lt;br /&gt;
#define  ID_CHRS        MAKE_ID(&#039;C&#039;,&#039;H&#039;,&#039;R&#039;,&#039;S&#039;)&lt;br /&gt;
&lt;br /&gt;
struct IFFParseIFace *IIFFParse;&lt;br /&gt;
&lt;br /&gt;
UBYTE mytext[]=&amp;quot;This FTXT written to clipboard by clipftxt example.\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
        struct IFFHandle   *iff = NULL;&lt;br /&gt;
        struct ContextNode *cn;&lt;br /&gt;
        long                error=0, unitnumber=0, rlen;&lt;br /&gt;
        int                 textlen;&lt;br /&gt;
        UBYTE               readbuf[RBUFSZ];&lt;br /&gt;
&lt;br /&gt;
        /* if not enough args or &#039;?&#039;, print usage */&lt;br /&gt;
        if(((argc)&amp;amp;&amp;amp;(argc&amp;lt;MINARGS))||(argv[argc-1][0]==&#039;?&#039;))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;%s\n&amp;quot;, usage);&lt;br /&gt;
                return RETURN_WARN;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        unitnumber = atoi(argv[1]);&lt;br /&gt;
&lt;br /&gt;
        struct Library *IFFParseBase = IExec-&amp;gt;OpenLibrary(&amp;quot;iffparse.library&amp;quot;, 50);&lt;br /&gt;
        IIFFParse = (struct IFFParseIFace*)IExec-&amp;gt;GetInterface(IFFParseBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
        &lt;br /&gt;
        if (IIFFParse == NULL)&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open iff parsing library.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Allocate IFF_File structure.&lt;br /&gt;
         */&lt;br /&gt;
        if (!(iff = IIFFParse-&amp;gt;AllocIFF ()))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;AllocIFF() failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Set up IFF_File for Clipboard I/O.&lt;br /&gt;
         */&lt;br /&gt;
        if (!(iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard (unitnumber)))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Clipboard open failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Opened clipboard unit %ld\n&amp;quot;, unitnumber);&lt;br /&gt;
&lt;br /&gt;
        IIFFParse-&amp;gt;InitIFFasClip (iff);&lt;br /&gt;
&lt;br /&gt;
#ifdef WRITEREAD&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Start the IFF transaction.&lt;br /&gt;
         */&lt;br /&gt;
        if (error = IIFFParse-&amp;gt;OpenIFF (iff, IFFF_WRITE))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;OpenIFF for write failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Write our text to the clipboard as CHRS chunk in FORM FTXT&lt;br /&gt;
         *&lt;br /&gt;
         * First, write the FORM ID (FTXT)&lt;br /&gt;
         */&lt;br /&gt;
        if(!(error = IIFFParse-&amp;gt;PushChunk(iff, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN)))&lt;br /&gt;
        {&lt;br /&gt;
                /* Now the CHRS chunk ID followed by the chunk data&lt;br /&gt;
                 * We&#039;ll just write one CHRS chunk.&lt;br /&gt;
                 * You could write more chunks.&lt;br /&gt;
                 */&lt;br /&gt;
                if(!(error = IIFFParse-&amp;gt;PushChunk(iff, 0, ID_CHRS, IFFSIZE_UNKNOWN)))&lt;br /&gt;
                {&lt;br /&gt;
                        /* Now the actual data (the text) */&lt;br /&gt;
                        textlen = strlen(mytext);&lt;br /&gt;
                        if(IIFFParse-&amp;gt;WriteChunkBytes(iff, mytext, textlen) != textlen)&lt;br /&gt;
                        {&lt;br /&gt;
                                IDOS-&amp;gt;Printf(&amp;quot;Error writing CHRS data.\n&amp;quot;);&lt;br /&gt;
                                error = IFFERR_WRITE;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
                if(!error) error = IIFFParse-&amp;gt;PopChunk(iff);&lt;br /&gt;
        }&lt;br /&gt;
        if(!error) error = IIFFParse-&amp;gt;PopChunk(iff);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        if(error)&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;IFF write failed, error %ld: %s\n&amp;quot;,&lt;br /&gt;
                             error, errormsgs[-error - 1]);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Wrote text to clipboard as FTXT\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Now let&#039;s close it, then read it back&lt;br /&gt;
         * First close the write handle, then close the clipboard&lt;br /&gt;
         */&lt;br /&gt;
        IIFFParse-&amp;gt;CloseIFF(iff);&lt;br /&gt;
        if (iff-&amp;gt;iff_Stream) IIFFParse-&amp;gt;CloseClipboard ((struct ClipboardHandle *)&lt;br /&gt;
                                                        iff-&amp;gt;iff_Stream);&lt;br /&gt;
&lt;br /&gt;
        if (!(iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard (unitnumber)))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Reopen of Clipboard failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Reopened clipboard unit %ld\n&amp;quot;, unitnumber);&lt;br /&gt;
&lt;br /&gt;
#endif /* WRITEREAD */&lt;br /&gt;
&lt;br /&gt;
        if (error = IIFFParse-&amp;gt;OpenIFF (iff, IFFF_READ))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;OpenIFF for read failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Tell iffparse we want to stop on FTXT CHRS chunks */&lt;br /&gt;
        if (error = IFFParse-&amp;gt;StopChunk(iff, ID_FTXT, ID_CHRS))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;StopChunk failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Find all of the FTXT CHRS chunks */&lt;br /&gt;
        while(1)&lt;br /&gt;
        {&lt;br /&gt;
                error = IIFFParse-&amp;gt;ParseIFF(iff,IFFPARSE_SCAN);&lt;br /&gt;
                if(error == IFFERR_EOC) continue;       /* enter next context */&lt;br /&gt;
                else if(error) break;&lt;br /&gt;
&lt;br /&gt;
                /* We only asked to stop at FTXT CHRS chunks&lt;br /&gt;
                 * If no error we&#039;ve hit a stop chunk&lt;br /&gt;
                 * Read the CHRS chunk data&lt;br /&gt;
                 */&lt;br /&gt;
                cn = IIFFParse-&amp;gt;CurrentChunk(iff);&lt;br /&gt;
&lt;br /&gt;
                if((cn)&amp;amp;&amp;amp;(cn-&amp;gt;cn_Type == ID_FTXT)&amp;amp;&amp;amp;(cn-&amp;gt;cn_ID == ID_CHRS))&lt;br /&gt;
                {&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;CHRS chunk contains:\n&amp;quot;);&lt;br /&gt;
                        while((rlen = IIFFParse-&amp;gt;ReadChunkBytes(iff,readbuf,RBUFSZ)) &amp;gt; 0)&lt;br /&gt;
                        {&lt;br /&gt;
                                IDOS-&amp;gt;Write(Output(),readbuf,rlen);&lt;br /&gt;
                        }&lt;br /&gt;
                        if(rlen &amp;lt; 0)    error = rlen;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if((error)&amp;amp;&amp;amp;(error != IFFERR_EOF))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf (&amp;quot;IFF read failed, error %ld: %s\n&amp;quot;,&lt;br /&gt;
                              error, errormsgs[-error - 1]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
bye:&lt;br /&gt;
        if (iff)&lt;br /&gt;
        {&lt;br /&gt;
                /*&lt;br /&gt;
                 * Terminate the IFF transaction with the stream.  Free&lt;br /&gt;
                 * all associated structures.&lt;br /&gt;
                 */&lt;br /&gt;
                IIFFParse-&amp;gt;CloseIFF (iff);&lt;br /&gt;
&lt;br /&gt;
                /*&lt;br /&gt;
                 * Close the clipboard stream&lt;br /&gt;
                 */&lt;br /&gt;
                if (iff-&amp;gt;iff_Stream)&lt;br /&gt;
                        IIFFParse-&amp;gt;CloseClipboard ((struct ClipboardHandle *)&lt;br /&gt;
                                                   iff-&amp;gt;iff_Stream);&lt;br /&gt;
                /*&lt;br /&gt;
                 * Free the IFF_File structure itself.&lt;br /&gt;
                 */&lt;br /&gt;
                IIFFParse-&amp;gt;FreeIFF (iff);&lt;br /&gt;
        }&lt;br /&gt;
        IExec-&amp;gt;DropInterface((struct Interface*)IIFFParse);&lt;br /&gt;
        IExec-&amp;gt;CloseLibrary (IFFParseBase);&lt;br /&gt;
&lt;br /&gt;
        return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== IFF Scanner Example ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Sift.c&amp;quot; lists the type and size of every chunk in an IFF file and, and checks the IFF file for correct syntax. You should use &amp;quot;Sift&amp;quot; to check IFF files created by your programs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 *&lt;br /&gt;
 * sift.c:       Takes any IFF file and tells you what&#039;s in it.  Verifies syntax and all that cool stuff.&lt;br /&gt;
 *&lt;br /&gt;
 * Usage: sift -c          ; For clipboard scanning&lt;br /&gt;
 *    or  sift &amp;lt;file&amp;gt;      ; For DOS file scanning&lt;br /&gt;
 *&lt;br /&gt;
 * Reads the specified stream and prints an IFFCheck-like listing of the contents of the IFF file, if any.&lt;br /&gt;
 * Stream is a DOS file for &amp;lt;file&amp;gt; argument, or is the clipboard&#039;s primary clip for -c.&lt;br /&gt;
 * This program must be run from a CLI.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MINARGS 2&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR usage = &amp;quot;Usage: sift IFFfilename (or -c for clipboard)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void PrintTopChunk(struct IFFHandle *);  /* prototype for our function */&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Text error messages for possible IFFERR_#? returns from various IFF routines.  To get the index into&lt;br /&gt;
 * this array, take your IFFERR code, negate it, and subtract one.&lt;br /&gt;
 *  idx = -error - 1;&lt;br /&gt;
 */&lt;br /&gt;
char *errormsgs[] = {&lt;br /&gt;
     &amp;quot;End of file (not an error).&amp;quot;, &amp;quot;End of context (not an error).&amp;quot;, &amp;quot;No lexical scope.&amp;quot;,&lt;br /&gt;
     &amp;quot;Insufficient memory.&amp;quot;, &amp;quot;Stream read error.&amp;quot;, &amp;quot;Stream write error.&amp;quot;,&lt;br /&gt;
     &amp;quot;Stream seek error.&amp;quot;, &amp;quot;File is corrupt.&amp;quot;, &amp;quot;IFF syntax error.&amp;quot;,&lt;br /&gt;
     &amp;quot;Not an IFF file.&amp;quot;, &amp;quot;Required call-back hook missing.&amp;quot;, &amp;quot;Return to client.  You should never see this.&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct IFFParseIFace *IIFFParse = NULL;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
        struct IFFHandle    *iff = NULL;&lt;br /&gt;
        int32                error;&lt;br /&gt;
        int16                cbio;&lt;br /&gt;
&lt;br /&gt;
        /* if not enough args or &#039;?&#039;, print usage */&lt;br /&gt;
        if(((argc)&amp;amp;&amp;amp;(argc&amp;lt;MINARGS))||(argv[argc-1][0]==&#039;?&#039;))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;%s\n&amp;quot;, usage);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Check to see if we are doing I/O to the Clipboard. */&lt;br /&gt;
        cbio = (argv[1][0] == &#039;-&#039;  &amp;amp;&amp;amp;  argv[1][1] == &#039;c&#039;);&lt;br /&gt;
&lt;br /&gt;
        struct Library *IFFParseBase = IExec-&amp;gt;OpenLibrary(&amp;quot;iffparse.library&amp;quot;, 50);&lt;br /&gt;
        IIFFParse = (struct IFFParseIFace*)IExec-&amp;gt;GetInterface(IFFParseBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
        if (IIFFParse == NULL)&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open iff parsing library.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Allocate IFF_File structure. */&lt;br /&gt;
        if (!(iff = IIFFParse-&amp;gt;AllocIFF()))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;AllocIFF() failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Internal support is provided for both AmigaDOS files, and the clipboard.device. This bizarre&lt;br /&gt;
         * &#039;if&#039; statement performs the appropriate machinations for each case.&lt;br /&gt;
         */&lt;br /&gt;
        if (cbio)&lt;br /&gt;
        {&lt;br /&gt;
                /*&lt;br /&gt;
                 * Set up IFF_File for Clipboard I/O.&lt;br /&gt;
                 */&lt;br /&gt;
                if (!(iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard(PRIMARY_CLIP)))&lt;br /&gt;
                {&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;Clipboard open failed.\n&amp;quot;);&lt;br /&gt;
                        goto bye;&lt;br /&gt;
                }&lt;br /&gt;
                IIFFParse-&amp;gt;InitIFFasClip(iff);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                /* Set up IFF_File for AmigaDOS I/O.  */&lt;br /&gt;
                if (!(iff-&amp;gt;iff_Stream = IDOS-&amp;gt;Open(argv[1], MODE_OLDFILE)))&lt;br /&gt;
                {&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;File open failed.\n&amp;quot;);&lt;br /&gt;
                        goto bye;&lt;br /&gt;
                }&lt;br /&gt;
                IIFFParse-&amp;gt;InitIFFasDOS(iff);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Start the IFF transaction. */&lt;br /&gt;
        if (error = IIFFParse-&amp;gt;OpenIFF(iff, IFFF_READ))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;OpenIFF failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
                /*&lt;br /&gt;
                 * The interesting bit. IFFPARSE_RAWSTEP permits us to have precision monitoring of the&lt;br /&gt;
                 * parsing process, which is necessary if we wish to print the structure of an IFF file.&lt;br /&gt;
                 * ParseIFF() with _RAWSTEP will return the following things for the following reasons:&lt;br /&gt;
                 *&lt;br /&gt;
                 * Return code:                 Reason:&lt;br /&gt;
                 * 0                            Entered new context.&lt;br /&gt;
                 * IFFERR_EOC                   About to leave a context.&lt;br /&gt;
                 * IFFERR_EOF                   Encountered end-of-file.&lt;br /&gt;
                 * &amp;lt;anything else&amp;gt;              A parsing error.&lt;br /&gt;
                 */&lt;br /&gt;
                error = IIFFParse-&amp;gt;ParseIFF(iff, IFFPARSE_RAWSTEP);&lt;br /&gt;
&lt;br /&gt;
                /*&lt;br /&gt;
                 * Since we&#039;re only interested in when we enter a context, we &amp;quot;discard&amp;quot; end-of-context&lt;br /&gt;
                 * (_EOC) events.&lt;br /&gt;
                 */&lt;br /&gt;
                if (error == IFFERR_EOC)&lt;br /&gt;
                        continue;&lt;br /&gt;
                else if (error)&lt;br /&gt;
                {&lt;br /&gt;
                        /*&lt;br /&gt;
                         * Leave the loop if there is any other error.&lt;br /&gt;
                         */&lt;br /&gt;
                        break;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                /* If we get here, error was zero. Print out the current state of affairs. */&lt;br /&gt;
                PrintTopChunk(iff);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * If error was IFFERR_EOF, then the parser encountered the end of&lt;br /&gt;
         * the file without problems. Otherwise, we print a diagnostic.&lt;br /&gt;
         */&lt;br /&gt;
        if (error == IFFERR_EOF)&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;File scan complete.\n&amp;quot;);&lt;br /&gt;
        else&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;File scan aborted, error %ld: %s\n&amp;quot;,&lt;br /&gt;
                        error, errormsgs[-error - 1]);&lt;br /&gt;
&lt;br /&gt;
bye:&lt;br /&gt;
        if (iff)&lt;br /&gt;
        {&lt;br /&gt;
                /* Terminate the IFF transaction with the stream. Free all associated structures. */&lt;br /&gt;
                IIFFParse-&amp;gt;CloseIFF(iff);&lt;br /&gt;
&lt;br /&gt;
                /*&lt;br /&gt;
                 * Close the stream itself.&lt;br /&gt;
                 */&lt;br /&gt;
                if (iff-&amp;gt;iff_Stream)&lt;br /&gt;
                {&lt;br /&gt;
                        if (cbio)&lt;br /&gt;
                                IIFFParse-&amp;gt;CloseClipboard((struct ClipboardHandle *)iff-&amp;gt;iff_Stream);&lt;br /&gt;
                        else&lt;br /&gt;
                                IDOS-&amp;gt;Close(iff-&amp;gt;iff_Stream);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                /* Free the IFF_File structure itself. */&lt;br /&gt;
                IIFFParse-&amp;gt;FreeIFF(iff);&lt;br /&gt;
        }&lt;br /&gt;
        IExec-&amp;gt;DropInterface((struct Interface*)IIFFParse);&lt;br /&gt;
        IExec-&amp;gt;CloseLibrary(IFFParseBase);&lt;br /&gt;
&lt;br /&gt;
        return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void PrintTopChunk(struct IFFHandle *iff)&lt;br /&gt;
{&lt;br /&gt;
        struct ContextNode      *top;&lt;br /&gt;
        int32                   i;&lt;br /&gt;
        char                    idbuf[5];&lt;br /&gt;
&lt;br /&gt;
        /* Get a pointer to the context node describing the current context. */&lt;br /&gt;
        if (!(top = IIFFParse-&amp;gt;CurrentChunk(iff)))&lt;br /&gt;
                return;&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Print a series of dots equivalent to the current nesting depth of chunks processed so far.&lt;br /&gt;
         * This will cause nested chunks to be printed out indented.&lt;br /&gt;
         */&lt;br /&gt;
        for (i = iff-&amp;gt;iff_Depth;  i--; )&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;. &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        /* Print out the current chunk&#039;s ID and size. */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;%s %ld &amp;quot;, IIFFParse-&amp;gt;IDtoStr(top-&amp;gt;cn_ID, idbuf), top-&amp;gt;cn_Size);&lt;br /&gt;
&lt;br /&gt;
        /* Print the current chunk&#039;s type, with a newline. */&lt;br /&gt;
        IDOS-&amp;gt;Printf(IIFFParse-&amp;gt;IDtoStr(top-&amp;gt;cn_Type, idbuf));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the IFFParse functions discussed in this article. Further information about these and other IFFParse functions can be found in the SDK.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AllocIFF()&lt;br /&gt;
| Creates an IFFHandle structure.&lt;br /&gt;
|-&lt;br /&gt;
| FreeIFF()&lt;br /&gt;
| Frees the IFFHandle structure created with AllocIFF().&lt;br /&gt;
|-&lt;br /&gt;
| OpenIFF()&lt;br /&gt;
| Initialize an IFFHandle structure to read or write an IFF stream.&lt;br /&gt;
|-&lt;br /&gt;
| CloseIFF()&lt;br /&gt;
| Closes an IFF context.&lt;br /&gt;
|-&lt;br /&gt;
| InitIFF()&lt;br /&gt;
| Initialize an IFFHandle as a user-defined stream.&lt;br /&gt;
|-&lt;br /&gt;
| InitIFFasDOS()&lt;br /&gt;
| Initialize an IFFHandle as an AmigaDOS stream.&lt;br /&gt;
|-&lt;br /&gt;
| InitIFFasClip()&lt;br /&gt;
| Initialize an IFFHandle as a clipboard stream.&lt;br /&gt;
|-&lt;br /&gt;
| OpenClipboard()&lt;br /&gt;
| Create a handle on a clipboard unit for InitIFFasClip().&lt;br /&gt;
|-&lt;br /&gt;
| ParseIFF()&lt;br /&gt;
| Parse an IFF file from an IFFHandle stream.&lt;br /&gt;
|-&lt;br /&gt;
| ReadChunkBytes()&lt;br /&gt;
| Read bytes from the current chunk into a buffer.&lt;br /&gt;
|-&lt;br /&gt;
| ReadChunkRecords()&lt;br /&gt;
| Read record elements from the current chunk into a buffer.&lt;br /&gt;
|-&lt;br /&gt;
| StopChunk()&lt;br /&gt;
| Declare a chunk that should cause ParseIFF() to return.&lt;br /&gt;
|-&lt;br /&gt;
| CurrentChunk()&lt;br /&gt;
| Get the context node for the current chunk.&lt;br /&gt;
|-&lt;br /&gt;
| PropChunk()&lt;br /&gt;
| Specify a property chunk to store.&lt;br /&gt;
|-&lt;br /&gt;
| FindProp()&lt;br /&gt;
| Search for a stored property in a given context.&lt;br /&gt;
|-&lt;br /&gt;
| CollectionChunk()&lt;br /&gt;
| Declare a chunk type for collection.&lt;br /&gt;
|-&lt;br /&gt;
| FindCollection()&lt;br /&gt;
| Get a pointer to the current list of collection items.&lt;br /&gt;
|-&lt;br /&gt;
| StopOnExit()&lt;br /&gt;
| Declare a stop condition for exiting a chunk.&lt;br /&gt;
|-&lt;br /&gt;
| EntryHandler()&lt;br /&gt;
| Add an entry handler to the IFFHandle context.&lt;br /&gt;
|-&lt;br /&gt;
| ExitHandler()&lt;br /&gt;
| Add an exit handler to the IFFHandle context.&lt;br /&gt;
|-&lt;br /&gt;
| PushChunk()&lt;br /&gt;
| Push a given context node onto the top of the context stack.&lt;br /&gt;
|-&lt;br /&gt;
| PopChunk()&lt;br /&gt;
| Pop the top context node off of the context stack.&lt;br /&gt;
|-&lt;br /&gt;
| CurrentChunk()&lt;br /&gt;
| Get the top context node for the current chunk.&lt;br /&gt;
|-&lt;br /&gt;
| ParentChunk()&lt;br /&gt;
| Get the nesting context node for a given chunk.&lt;br /&gt;
|-&lt;br /&gt;
| AllocLocalItem()&lt;br /&gt;
| Create a LocalContextItem (LCI) structure.&lt;br /&gt;
|-&lt;br /&gt;
| LocalItemData()&lt;br /&gt;
| Returns a pointer to the user data of a LocalContextItem (LCI).&lt;br /&gt;
|-&lt;br /&gt;
| StoreLocalItem()&lt;br /&gt;
| Insert a LocalContextItem (LCI).&lt;br /&gt;
|-&lt;br /&gt;
| StoreItemInContext()&lt;br /&gt;
| Store a LocalContextItem in a given context node.&lt;br /&gt;
|-&lt;br /&gt;
| FindPropContext()&lt;br /&gt;
| Find the property context for the current state.&lt;br /&gt;
|-&lt;br /&gt;
| FindLocalItem()&lt;br /&gt;
| Return a LocalContextItem from the context stack.&lt;br /&gt;
|-&lt;br /&gt;
| FreeLocalItem()&lt;br /&gt;
| Free a LocalContextItem (LCI) created with AllocLocalItem().&lt;br /&gt;
|-&lt;br /&gt;
| SetLocalItemPurge()&lt;br /&gt;
| Set purge vector for a local context item.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Fredrik Wikstrom</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=IFFParse_Library&amp;diff=9243</id>
		<title>IFFParse Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=IFFParse_Library&amp;diff=9243"/>
		<updated>2017-10-18T07:18:16Z</updated>

		<summary type="html">&lt;p&gt;Fredrik Wikstrom: Fixed indentation.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== IFFParse Library ==&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;iffparse.library&#039;&#039; was created to help simplify the job of parsing IFF files. Unlike other IFF libraries, iffparse.library is not form-specific. This means that the job of interpreting the structure and contents of the IFF file is up to you. Previous IFF file parsers were either simple but not general, or general but not simple. IFFParse tries to be both simple &#039;&#039;and&#039;&#039; general.&lt;br /&gt;
&lt;br /&gt;
== The Structure of IFF Files ==&lt;br /&gt;
&lt;br /&gt;
Many people have a misconception that IFF means image files. This is not the case. IFF (short for Interchange File Format) is a method of portably storing structured information in machine-readable form. The actual information can be anything, but the manner in which it is stored is very specifically detailed. This specification is the IFF standard.&lt;br /&gt;
&lt;br /&gt;
The IFF standard was originally designed in 1985 by Electronic Arts in conjunction with a committee of developers. The full standard along with file descriptions and sample code is available at [[IFF_Standard|IFF Standard]].&lt;br /&gt;
&lt;br /&gt;
The goal of the IFF standard is to allow customers to move their own data between independently developed software products. The types of data objects to exchange are open-ended and currently include plain and formatted text, raster and structured graphics, fonts, music, sound effects, musical instrument descriptions, and animation. IFF addresses these needs by defining a standard for self-identifying file structures and rules for accessing these files.&lt;br /&gt;
&lt;br /&gt;
=== Chunks: The Building Blocks of IFF ===&lt;br /&gt;
&lt;br /&gt;
IFF files contain various types and amounts of data grouped in data &#039;&#039;chunks&#039;&#039;, each starting with a four-letter ASCII identifier (the chunk ID) followed by a 32-bit length count (the chunk size). The identifier and length count make it possible for IFF readers to skip over chunks that they don’t understand or don’t care about, and extract only the information they need. It may be helpful to think of these chunks as the building blocks of an IFF file.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig33-1.png|frame|center|The Chunk - The Building Block of IFF]]&lt;br /&gt;
&lt;br /&gt;
The ‘CKID’ (chunk ID) characters of a real chunk will be a four letter identifier such as ‘BODY’ or ‘CMAP’, specifying the type and format of data stored in the chunk. Most chunks contain a simple defined grouping of byte, word, and long variables similar to the contents of a C structure. Others such as sound sample and bitmap image body chunks, contain a stream of compressed data.&lt;br /&gt;
&lt;br /&gt;
Chunk writing is fairly straightforward with the one caveat that all chunks must be word-aligned. Therefore an odd-length chunk must be followed by a pad byte of zero (which is not counted in the size of the chunk). When chunks are nested, the enclosing chunk must state the total size of its composite contents (including any pad bytes).&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About Chunk Length|text=Every IFF data chunk begins with a 4-character identifier field followed by a 32-bit size field (these 8 bytes are sometimes referred to as the chunk header). The size field is a count of the rest of the data bytes in the chunk, &#039;&#039;not&#039;&#039; including any pad byte. Hence, the total space occupied by a chunk is given by its size field (rounded to the nearest even number) + 8 bytes for the chunk header}}&lt;br /&gt;
&lt;br /&gt;
=== Composite Data Types ===&lt;br /&gt;
&lt;br /&gt;
Standard IFF files generally contain a variable number of chunks within one of the standard IFF composite data types (which may be thought of as chunks which contain other chunks). The IFF specification defines three basic composite data types, ‘FORM’, ‘CAT ’, and ‘LIST’. A special composite data type, the ‘PROP’, is found only inside a LIST.&lt;br /&gt;
&lt;br /&gt;
Usage of Composite IFF Types&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| FORM || A grouping of chunks which describe one set of data&lt;br /&gt;
|-&lt;br /&gt;
| LIST || A grouping of the same type of FORMs with shared properties in a PROP&lt;br /&gt;
|-&lt;br /&gt;
| PROP || May appear in a LIST to define chunks shared by several FORMs&lt;br /&gt;
|-&lt;br /&gt;
| CAT || A concatenation of related FORMs or LISTs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The special IFF composite data types, like simple chunks, start with a 4-character identifier (such as ‘FORM’), followed by a 32-bit length. But their data begins with a second 4-character identifier that tells you what type of data is in the composite. For example, a FORM ILBM contains chunks describing a bitmap image, and a FORM FTXT contains chunks describing formatted text.&lt;br /&gt;
&lt;br /&gt;
It may help to think of each composite data type as a box containing chunks, the IFF building blocks. The following figure shows a diagram of a typical composite.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig33-2.png|frame|center|The FORM – The Most Common IFF File Type]]&lt;br /&gt;
&lt;br /&gt;
The example FORM in the previous figure is outlined below showing the size of chunks within the FORM. Notice that the size of a composite &#039;&#039;includes&#039;&#039; its second 4-character identifier (shown below as ‘NAME’).&lt;br /&gt;
&lt;br /&gt;
 . FORM 72 NAME  (72 is the size of the FORM starting with the “N” of NAME)&lt;br /&gt;
 . . CKID 22     (then 22 bytes of data, so chunk header + chunk size is 30)&lt;br /&gt;
 . . CKID 10     (then 10 bytes of data, so chunk header + chunk size is 18)&lt;br /&gt;
 . . CKID 12     (then 12 bytes of data, so chunk header + chunk size is 20)&lt;br /&gt;
&lt;br /&gt;
{{Note|text=In the example above, indentation represents the nesting of the chunks within the FORM. Real FORMs and chunks would have different four-character identifiers rather than ‘NAME’ and ‘CKID’.}}&lt;br /&gt;
&lt;br /&gt;
Any odd-length chunks must have a pad byte of zero at the end of the chunk. As shown below, this pad byte is not counted in the size of the chunk but &#039;&#039;is&#039;&#039; counted in the size of any composite types (such as FORM) that contain an odd-length chunk. &#039;&#039;&#039;Warning:&#039;&#039;&#039; some IFF readers and writers do not deal with this properly.&lt;br /&gt;
&lt;br /&gt;
 . FORM 72 NAME  (72 is the size of the FORM starting with the “N” of NAME)&lt;br /&gt;
 . . CKID 21     (then 21 bytes of data, so chunk header + chunk size is 29)&lt;br /&gt;
 . . 0           (pad byte of zero, size 1)&lt;br /&gt;
 . . CKID 10     (then 10 bytes of data, so chunk header + chunk size is 18)&lt;br /&gt;
 . . CKID 12     (then 12 bytes of data, so chunk header + chunk size is 20)&lt;br /&gt;
&lt;br /&gt;
Most IFF files are of the composite type FORM. Generally, a FORM is a simple grouping of chunks that provide information about some data, and the data itself. Although some standard chunks have common usage within different FORMS, the identity and format of most chunks within a FORM are relative to the definition and specification of the FORM. For example, a CRNG chunk in a FORM ILBM may have a totally different format and contents than a chunk named CRNG in a different type of FORM.&lt;br /&gt;
&lt;br /&gt;
One of the most popular IFF FORMs that has been defined is the ILBM standard. This is how most Amiga bitmap imagery is stored. Since this is the most common IFF file, it is used frequently as an example.&lt;br /&gt;
&lt;br /&gt;
Here is the output of a program called “Sift.c” that displays a text description of the contents of an IFF file ([[#IFF Scanner Example|Sift.c]] is listed at the end of this article). The file shown below is a fairly simple ILBM.&lt;br /&gt;
&lt;br /&gt;
 . FORM 11068 ILBM&lt;br /&gt;
 . . BMHD 20&lt;br /&gt;
 . . CAMG 4&lt;br /&gt;
 . . CMAP 12&lt;br /&gt;
 . . BODY 10995&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Computing the Size of a FORM|text=The size of the FORM (11,068 bytes) is equal to the sum of the sizes stated in each chunk contained within the FORM, plus 8 bytes for the overhead of each chunk header (4 bytes for the 4-character chunk ID, and 4 bytes for the 32-bit chunk size), plus 4 bytes for the FORM’s own 4-character ID (‘ILBM’), plus 1 byte more for each pad byte that follow any odd-length chunks.}}&lt;br /&gt;
&lt;br /&gt;
== Parsing an IFF File ==&lt;br /&gt;
&lt;br /&gt;
Chunk reading requires a parser to scan each chunk and dispatch the proper access/conversion procedure. For a simple IFF file, such parsing may be relatively easy, consisting mainly reading in the data of desired chunks, and seeking over unwanted chunks (and the pad byte after odd-length chunks). Interpreting nested chunks is more complex, and requires a method for keeping track of the current context, i.e., the data which is still relevant at any particular depth into the nest. The original IFF specifications compare an IFF file to a C program. Such a metaphor can be useful in understanding the scope of of the chunks in an IFF file.&lt;br /&gt;
&lt;br /&gt;
The IFFParse library addresses general IFF parsing requirements by providing a run-time library which can extract the chunks you want from an IFF file, with the ability to pass control to you when it reaches a chunk that requires special processing such as decompression. IFFParse also understands complex nested IFF formats and will keep track of the context for you.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functions and Structures of IFFParse Library ===&lt;br /&gt;
&lt;br /&gt;
The structures and flags of the IFFParse library are defined in the include files &amp;amp;lt;libraries/iffparse.h&amp;amp;gt; and &amp;amp;lt;libraries/iffparse.i&amp;amp;gt;. IFF files are manipulated through a structure called an IFFHandle. Only some of the fields in the IFFHandle are publicly documented. The rest are managed internally by IFFParse. This handle is passed to all IFFParse functions, and contains the current parse state and position in the file. An IFFHandle is obtained by calling AllocIFF(), and freed through FreeIFF(). This is the only legal way to obtain and dispose of an IFFHandle.&lt;br /&gt;
&lt;br /&gt;
The public portion of if IFFHandle is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Structure associated with an active IFF stream.&lt;br /&gt;
 * &amp;quot;iff_Stream&amp;quot; is a value used by the client&#039;s read/write/seek functions -&lt;br /&gt;
 * it will not be accessed by the library itself and can have any value&lt;br /&gt;
 * (could even be a pointer or a BPTR).&lt;br /&gt;
 */&lt;br /&gt;
struct IFFHandle {&lt;br /&gt;
        ULONG   iff_Stream;&lt;br /&gt;
        ULONG   iff_Flags;&lt;br /&gt;
        LONG    iff_Depth;      /*  Depth of context stack.  */&lt;br /&gt;
        /*  There are private fields hiding here.  */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stream Management ==&lt;br /&gt;
&lt;br /&gt;
A stream is a linear array of bytes that may be accessed sequentially or randomly. DOS files are streams. IFFParse uses Hook structures (defined in &amp;amp;lt;utility/hooks.h&amp;amp;gt;) to implement a general stream management facility. This allows the IFFParse library to read, write, and seek any type of file handle or device by using an application-provided hook function to open, read, write, seek and close the stream.&lt;br /&gt;
&lt;br /&gt;
Built on top of this facility, IFFParse has two internal stream managers: one for unbuffered DOS files (AmigaDOS filehandles), and one for the Clipboard.&lt;br /&gt;
&lt;br /&gt;
=== Initialization ===&lt;br /&gt;
&lt;br /&gt;
As shown above, the IFFHandle structure contains the public field iff_Stream. This is a longword that must be initialized to contain something meaningful to the stream manager. IFFParse never looks at this field itself (at least not directly). Your iff_Stream may be an AmigaDOS filehandle, or an IFFParse ClipboardHandle, or a custom stream of any type if you provide your own custom stream handler (see the section on [[#Custom Stream Handlers|Custom Stream Handlers]]). You must initialize your IFFHandle structure’s iff_Stream to your stream, and then initialize the IFFHandle’s flags and stream hook.&lt;br /&gt;
&lt;br /&gt;
Three sample initializations are shown here. In the case of the internal DOS stream manager, iff_Stream is an AmigaDOS filehandle as returned by Open():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) IDOS-&amp;gt;Open (&amp;quot;filename&amp;quot;, MODE_OLDFILE);&lt;br /&gt;
if(iff-&amp;gt;iff_Stream) IIFFParse-&amp;gt;InitIFFasDOS (iff);  /* use internal DOS stream manager */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the case of the internal Clipboard stream manager, iff_Stream is a pointer to an IFFParse ClipboardHandle structure (the OpenClipboard() call used here is a function of iffparse.library, not the clipboard.device):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard (PRIMARY_CLIP);&lt;br /&gt;
IIFFParse-&amp;gt;InitIFFasClip (iff); /* use internal Clipboard stream manager  */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When using a custom handle such as an fopen() file handle, or a device other than the clipboard, you must provide your own flags and stream handler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) OpenMyCustomStream(&amp;quot;foo&amp;quot;);&lt;br /&gt;
IIFFParse-&amp;gt;InitIFF (iff, IFFF_FSEEK | IFFF_RSEEK, &amp;amp;mystreamhook);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
IFFParse “knows” the seek capabilities of DOS and ClipboardHandle streams, so InitIFFasDOS() and InitIFFasClip() set the flags for you.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=You May Change the Seek Bits in iff_Flags|text=IFFParse sets IFFF_FSEEK | IFFF_RSEEK for DOS files. This is not always true (e.g., pipes). If you know that a DOS file has different seek characteristics, your application may correct the seek bits in iff_Flags after calling InitIFFasDOS().}}&lt;br /&gt;
&lt;br /&gt;
When using InitIFF() to provide a custom handler, you must also provide flags to tell IFFParse the capabilities of your stream. The flags are:&lt;br /&gt;
&lt;br /&gt;
; IFFF_FSEEK&lt;br /&gt;
: This stream has forward-seek capability only.&lt;br /&gt;
&lt;br /&gt;
; IFFF_RSEEK&lt;br /&gt;
: This stream has random-seek capability. IFFF_RSEEK tends to imply IFFF_FSEEK, but it’s best to specify both.&lt;br /&gt;
&lt;br /&gt;
If neither flag is specified, you’re telling IFFParse that it can’t seek through the stream.&lt;br /&gt;
&lt;br /&gt;
After your stream is initialized, call OpenIFF():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;OpenIFF (iff, IFFF_READ);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you establish a read/write mode (by passing IFFF_READ or IFFF_WRITE), you remain in that mode until you call CloseIFF().&lt;br /&gt;
&lt;br /&gt;
=== Termination ===&lt;br /&gt;
&lt;br /&gt;
Termination is simple. Just call CloseIFF(iff). This may be called at any time, and terminates IFFParse’s transaction with the stream. The stream itself is not closed. The IFFHandle may be reused; you may safely call OpenIFF() on it again. You are responsible for closing the streams you opened. A stream used in an IFFHandle must generally remain open until you CloseIFF().&lt;br /&gt;
&lt;br /&gt;
=== Custom Streams ===&lt;br /&gt;
&lt;br /&gt;
A custom stream handler can allow you (and iffparse.library) to use your compiler’s own file I/O functions such as fopen(), fread() and fwrite(), rather than the lower-level AmigaDOS equivalents Open(), Read(), and Write(). A custom stream handler could also be used to read or write IFF files from an Exec device or an unusual handler or file system.&lt;br /&gt;
&lt;br /&gt;
If you install your own stream handler function, iffparse.library will call your function whenever it needs to read, write, or seek on your file. Your stream handler function will then perform these stream actions for iffparse.library. See the “Custom Stream Handlers” section for more information on custom stream handlers.&lt;br /&gt;
&lt;br /&gt;
== Parsing and Writing IFF ==&lt;br /&gt;
&lt;br /&gt;
For information on parsing IFF see [[Parsing_IFF|Parsing IFF]].&lt;br /&gt;
&lt;br /&gt;
For information on writing IFF see [[Writing_IFF|Writing IFF]].&lt;br /&gt;
&lt;br /&gt;
== Context Functions ==&lt;br /&gt;
&lt;br /&gt;
Internally, IFFParse maintains IFF nesting and scoping context via a &#039;&#039;context stack&#039;&#039;. The PushChunk() and PopChunk() functions get their names from this basic idea of the iffparse.library. Direct access to this stack is not allowed. However, many functions are provided to assist in examining and manipulating the context stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About the Context Stack|text=It is probably easier to think of a stack of blocks on a table in front of you when reading this discussion.}}&lt;br /&gt;
&lt;br /&gt;
As the nesting level increases (as would happen when parsing a nested LIST or FORM), the depth of the context stack increases; new elements are added to the top. When these contexts expire, the ContextNodes are deleted and the stack shrinks.&lt;br /&gt;
&lt;br /&gt;
=== Context Nodes ===&lt;br /&gt;
&lt;br /&gt;
The current context is said to be the top element on the stack. Contextual information is stored in a structure called a ContextNode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ContextNode {&lt;br /&gt;
        struct MinNode  cn_Node;&lt;br /&gt;
        LONG            cn_ID;&lt;br /&gt;
        LONG            cn_Type;&lt;br /&gt;
        LONG            cn_Size;        /*  Size of this chunk             */&lt;br /&gt;
        LONG            cn_Scan;        /*  # of bytes read/written so far */&lt;br /&gt;
        /*  There are private fields hiding here.  */&lt;br /&gt;
        };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CurrentChunk() ====&lt;br /&gt;
&lt;br /&gt;
You can obtain a pointer to the current ContextNode through the function CurrentChunk():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
currentnode = IIFFParse-&amp;gt;CurrentChunk (iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ContextNode tells you the type, ID, and size of the currently active chunk. If there is no currently active context, NULL is returned.&lt;br /&gt;
&lt;br /&gt;
==== ParentChunk() ====&lt;br /&gt;
&lt;br /&gt;
To find the parent of a context, you call ParentChunk() on the relevant ContextNode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
parentnode = IIFFParse-&amp;gt;ParentChunk(currentnode);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If there is no parent context, NULL is returned.&lt;br /&gt;
&lt;br /&gt;
=== The Default Context ===&lt;br /&gt;
&lt;br /&gt;
When you first obtain an IFFHandle through AllocIFF(), a hidden default context node is created. You cannot get direct access to this node through CurrentChunk() or ParentChunk(). However, using StoreLocalItem(), you can store information in this context.&lt;br /&gt;
&lt;br /&gt;
=== Context-Specific Data: LocalContextItems ===&lt;br /&gt;
&lt;br /&gt;
ContextNodes can contain application data specific to that context. These data objects are called LocalContextItems. LocalContextItems (LCIs) are a grey-box structure which contain a type, ID and identification field. LCIs are used to store context-sensitive data. The format of this data is application-defined. A ContextNode can contain as many LCIs as you want.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig33-3.png|frame|center|IFFParse Context Stack]]&lt;br /&gt;
&lt;br /&gt;
The previous figure shows the relationship between the IFFHandle, the ContextNodes, and the LCIs. The IFFHandle is the root of the tree, so to speak. ContextNodes “grow” out of the IFFHandle. In turn, LCIs &amp;quot;grow&amp;quot; out of the ContextNodes. What grows out of an LCI is client-defined.&lt;br /&gt;
&lt;br /&gt;
==== AllocLocalItem() ====&lt;br /&gt;
&lt;br /&gt;
To create an LCI, you use the function AllocLocalItem():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
lci = IIFFParse-&amp;gt;AllocLocalItem (type, id, ident, datasize);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If successful, you will be returned a pointer to an LCI having the specified type, ID, and identification values; and with datasize bytes of buffer space for your application to use.&lt;br /&gt;
&lt;br /&gt;
==== LocalItemData() ====&lt;br /&gt;
&lt;br /&gt;
To get a pointer to an LCIs data buffer, you use LocalItemData():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
buf = IIFFParse-&amp;gt;LocalItemData (lci);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may read and write the buffer to your heart’s content; it is yours. You should not, however, write beyond the end of the buffer. The size of the buffer is what you asked for when you called AllocLocalItem().&lt;br /&gt;
&lt;br /&gt;
=== Storing LCIs ===&lt;br /&gt;
&lt;br /&gt;
Once you’ve created and initialized an LCI, you’ll want to attach it to a ContextNode. Though a ContextNode can have many LCIs, a given LCI can be linked to only one ContextNode. Once linked, an LCI cannot be removed from a ContextNode (this may change in the future). Storing an LCI in a ContextNode is done with the functions StoreLocalItem() and StoreItemInContext().&lt;br /&gt;
&lt;br /&gt;
==== StoreLocalItem() ====&lt;br /&gt;
&lt;br /&gt;
The StoreLocalItem() function is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = StoreLocalItem (iff, lci, position);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The position argument determines where the LCI is stored. The possible values are IFFSLI_ROOT, IFFSLI_TOP, and IFFSLI_PROP.&lt;br /&gt;
&lt;br /&gt;
; IFFSLI_ROOT&lt;br /&gt;
: causes StoreLocalItem() to store your LCI in the default ContextNode.&lt;br /&gt;
&lt;br /&gt;
; IFFSLI_TOP&lt;br /&gt;
: gets your LCI stored in the top (current) ContextNode.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=The LCI Ends When the Current Context Ends|text=When the current context expires, your LCI will be deleted by the parser.}}&lt;br /&gt;
&lt;br /&gt;
IFFSLI_PROP causes your LCI to be stored in the topmost context from which a property would apply. This is usually the topmost FORM or LIST chunk. For example, suppose you had a deeply nested ILBM FORM, and you wanted to store the BMHD property in its correct context such that, when the current FORM context expired, the BMHD property would be deleted. IFFSLI_PROP will cause StoreLocalItem() to locate the proper context for such scoping, and store the LCI there. See the section on &amp;quot;Finding the Prop Context&amp;quot; for additional information on the scope of properties.&lt;br /&gt;
&lt;br /&gt;
==== StoreItemInContext() ====&lt;br /&gt;
&lt;br /&gt;
StoreItemInContext() is used when you already have a pointer to the ContextNode to which you want to attach your LCI. It is called like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IIFFParse-&amp;gt;StoreItemInContext (iff, lci, contextnode);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
StoreItemInContext() links your LCI into the specified ContextNode. Then it searches the ContextNode to see if there is another LCI with the same type, ID, and identification values. If so, the old one is deleted.&lt;br /&gt;
&lt;br /&gt;
==== FindLocalItem() ====&lt;br /&gt;
&lt;br /&gt;
After you’ve stored your LCI in a ContextNode, you will no doubt want to be able to find it again later. You do this with the function FindLocalItem(), which is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
lci = IIFFParse-&amp;gt;FindLocalItem (iff, type, id, ident);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FindLocalItem() attempts to locate an LCI having the specified type, ID, and identification values. The search proceeds as follows (refer to the previous figure to understand this better).&lt;br /&gt;
&lt;br /&gt;
FindLocalItem() starts at the top (current) ContextNode and searches all LCIs in that context. If no matching LCIs are found, it proceeds down the context stack to the next ContextNode and searches all its LCIs. The process repeats until it finds the desired LCI (whereupon it returns a pointer to it), or reaches the end without finding anything (where it returns NULL).&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Context Stack Position|text=LCIs higher in the stack will &amp;quot;override&amp;quot; lower LCIs with the same type, ID, and identification field. This is how property scoping is handled. As ContextNodes are popped off the context stack, all its LCIs are deleted as well. See the section on “Freeing LCIs” below for additional information on deleting LCIs.}}&lt;br /&gt;
&lt;br /&gt;
=== Some Interesting Internal Details ===&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=This section details some internal implementation details of iffparse.library which may help you to understand it better. Use of the following information to do “clever” things in an application is forbidden and unsupportable. Don’t even think about it.}}&lt;br /&gt;
&lt;br /&gt;
It turns out that StoredProperties, CollectionItems, and entry and exit handlers are all implemented using LCIs. For example, when you call FindProp(), you are actually calling a front-end to FindLocalItem(). The mysterious identification value (which has heretofore never been discussed) is a value which permits you to differentiate between LCIs having the same type and ID.&lt;br /&gt;
&lt;br /&gt;
For instance, suppose you called PropChunk(), asking it to store an ILBM BMHD. PropChunk() will install an entry handler in the form of an LCI, having type equal to ‘ILBM’, ID equal to ‘BMHD’, and an identification value of IFFLCI_ENTRYHANDLER.&lt;br /&gt;
&lt;br /&gt;
When an ILBM BMHD is encountered, the entry handler is called, and it creates and stores another LCI having type equal to ‘ILBM’, ID equal to ‘BMHD’ and an identification value of IFFLCI_PROP.&lt;br /&gt;
&lt;br /&gt;
Thus, when you call FindProp(), it merely calls FindLocalItem() with your type and ID, and supplies IFFLCI_PROP for the identification value.&lt;br /&gt;
&lt;br /&gt;
Therefore, handlers, StoredProperties, CollectionItems and your own custom LCIs can never be confused with each other, since they all have unique identification values. Since they are all handled (and searched for) in the same way, they all “override” each other in a consistent way. Just as StoredProperties higher in the context stack will be found and returned before identical ones in lower contexts, so will chunk handlers be found and invoked before ones lower on the context stack (recall FindLocalItem()’s search procedure).&lt;br /&gt;
&lt;br /&gt;
This means you can temporarily override a chunk handler by installing an identical handler in a higher context. The handler will persist until the context in which it is stored expires, after which, the original one regains scope.&lt;br /&gt;
&lt;br /&gt;
== Error Handling ==&lt;br /&gt;
&lt;br /&gt;
If at any time during reading or writing you encounter an error, the IFFHandle is left in an undefined state. Upon detection of an error, you should perform an abort sequence and CloseIFF() the IFFHandle. Once CloseIFF() has been called, the IFFHandle is restored to normalcy and may be reused.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
&lt;br /&gt;
This section discusses customizing of IFFParse data handling. For applications with special needs, IFFParse supports both custom stream handlers and custom chunk handlers.&lt;br /&gt;
&lt;br /&gt;
=== Custom Stream Handlers ===&lt;br /&gt;
&lt;br /&gt;
As discussed earlier, IFFParse contains built-in stream handlers for AmigaDOS file handles as returned by Open(), and for the clipboard.device. If you are using AmigaDOS filehandles or the clipboard.device, you need not supply a custom stream handler.&lt;br /&gt;
&lt;br /&gt;
If you wish to use your compiler’s own file I/O functions (such as fread() ) or need to read or write to an unusual handler or Exec device, you must provide a custom stream handler for your IFFHandle. Your custom stream handler will be called to perform all reads, writes, and seeks on your custom stream. The allows you to use compiler file I/O functions, Exec device commands, or any other method to perform the requested stream operations.&lt;br /&gt;
&lt;br /&gt;
If you are implementing your own custom stream handler, you will need to know the mechanics of hook call-backs, and how to interpret the parameters. An IFFParse custom stream handler is simply a function in your code that follows hook function conventions (Hook functions are also known as callback functions. See [[Utility_Library|Utility Library]] for more details).&lt;br /&gt;
&lt;br /&gt;
==== Installing a Custom Stream Handler ====&lt;br /&gt;
&lt;br /&gt;
To initialize your IFFHandle to point to your custom stream and stream handler, you must open your stream and place a pointer to the stream in your IFFHandle’s iff_Stream field. This pointer could be the return from fopen(), or an Exec device I/O request, or any other type of pointer which will provide your custom stream handler with a way to manage your stream. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) fopen(&amp;quot;foo&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, you must install your custom handler for this stream, and also tell IFFParse the seek capabilities of your stream. To install your custom stream handler, you must first set up a Hook structure (&amp;amp;lt;utility/hooks.h&amp;amp;gt;) to point to your stream handling function. Then use InitIFF() to install your stream handler and also tell IFFParse the seek capabilities of your stream. There is “some assembly required”.&lt;br /&gt;
&lt;br /&gt;
For an IFFParse custom stream hook the function prototype looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 mystreamhandler(struct Hook *hook, struct IFFHandle *iff, struct IFFStreamCmd *actionpkt);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A return of 0 indicates success. A non-zero return indicates an error.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
static int32 mystreamhandler&lt;br /&gt;
(&lt;br /&gt;
    struct Hook         *hook,&lt;br /&gt;
    struct IFFHandle    *iff,&lt;br /&gt;
    struct IFFStreamCmd *actionpkt&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
/*&lt;br /&gt;
 * Code to handle the stream commands - see end this section&lt;br /&gt;
 * for a complete example custom stream handler function.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Initialization of Hook structure for registerized handler function */&lt;br /&gt;
struct Hook mystreamhook&lt;br /&gt;
{&lt;br /&gt;
  { NULL },&lt;br /&gt;
  (HOOKFUNC) mystreamhandler,  /* h_Entry, registerized function entry */&lt;br /&gt;
  NULL,&lt;br /&gt;
  NULL&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Open custom stream and InitIFF to custom stream handler */&lt;br /&gt;
if ( iff-&amp;gt;iff_Stream = (ULONG) myopen(&amp;quot;foo&amp;quot;) )&lt;br /&gt;
        {&lt;br /&gt;
        IIFFParse-&amp;gt;InitIFF (iff, IFFF_FSEEK | IFFF_RSEEK, &amp;amp;mystreamhook);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Inside a Custom Stream Handler ====&lt;br /&gt;
&lt;br /&gt;
When the library calls your stream handler, you’ll be passed a pointer to the Hook structure (hook in the example used here), a pointer to the IFFHandle (iff), and a pointer to an IFFStreamCmd structure (actionpkt). Your stream pointer may be found in iff-&amp;amp;gt;iff_Stream where you previously stored it. The IFFStreamCmd (actionpkt) will describe the action that IFFParse needs you to perform on your stream:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Custom stream handler is passed struct IFFStreamCmd *actionpkt */&lt;br /&gt;
struct IFFStreamCmd {&lt;br /&gt;
        LONG    sc_Command;     /*  Operation to be performed (IFFCMD_) */&lt;br /&gt;
        APTR    sc_Buf;         /*  Pointer to data buffer              */&lt;br /&gt;
        LONG    sc_NBytes;      /*  Number of bytes to be affected      */&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
/* Possible call-back command values.  (Using 0 as the value for IFFCMD_INIT&lt;br /&gt;
 * was, in retrospect, probably a bad idea.)&lt;br /&gt;
 */&lt;br /&gt;
#define IFFCMD_INIT     0       /*  Prepare the stream for a session    */&lt;br /&gt;
#define IFFCMD_CLEANUP  1       /*  Terminate stream session            */&lt;br /&gt;
#define IFFCMD_READ     2       /*  Read bytes from stream              */&lt;br /&gt;
#define IFFCMD_WRITE    3       /*  Write bytes to stream               */&lt;br /&gt;
#define IFFCMD_SEEK     4       /*  Seek on stream                      */&lt;br /&gt;
#define IFFCMD_ENTRY    5       /*  You just entered a new context      */&lt;br /&gt;
#define IFFCMD_EXIT     6       /*  You&#039;re about to leave a context     */&lt;br /&gt;
#define IFFCMD_PURGELCI 7       /*  Purge a LocalContextItem            */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Your custom stream handler should perform the requested action on your custom stream, and then return 0 for success or an IFFParse error if an error occurred. The following code demonstrates a sample stream handler for a stream which was opened with a compiler’s fopen() buffered file I/O function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
static int32 mystreamhandler ( struct Hook *hook,&lt;br /&gt;
                              struct IFFHandle *iff,&lt;br /&gt;
                              struct IFFStreamCmd *actionpkt )&lt;br /&gt;
{&lt;br /&gt;
        FILE   *stream;&lt;br /&gt;
        int32  nbytes, error;&lt;br /&gt;
        uint8  *buf;&lt;br /&gt;
&lt;br /&gt;
        stream  = (FILE *) iff-&amp;gt;iff_Stream;     /* get your stream pointer */&lt;br /&gt;
        nbytes  = actionpkt-&amp;gt;sc_NBytes;         /* length for command */&lt;br /&gt;
        buf     = (uint8 *) actionpkt-&amp;gt;sc_Buf;  /* buffer for the command */&lt;br /&gt;
&lt;br /&gt;
        /* Now perform the action specified by the actionpkt-&amp;amp;gt;sc_Command */&lt;br /&gt;
&lt;br /&gt;
        switch (actionpkt-&amp;gt;sc_Command) {&lt;br /&gt;
        case IFFCMD_READ:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_READ means read sc_NBytes from the stream and place&lt;br /&gt;
                 * it in the memory pointed to by sc_Buf.  Be aware that&lt;br /&gt;
                 * sc_NBytes may be larger than can be contained in an int.&lt;br /&gt;
                 * This is important if you plan on recompiling this for&lt;br /&gt;
                 * 16-bit ints, since fread() takes int arguments.&lt;br /&gt;
                 *&lt;br /&gt;
                 * Any error code returned will be remapped by IFFParse into&lt;br /&gt;
                 * IFFERR_READ.&lt;br /&gt;
                 */&lt;br /&gt;
                error = (fread (buf, 1, nbytes, stream) != nbytes);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
        case IFFCMD_WRITE:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_WRITE is analogous to IFFCMD_READ.&lt;br /&gt;
                 *&lt;br /&gt;
                 * Any error code returned will be remapped by IFFParse into&lt;br /&gt;
                 * IFFERR_WRITE.&lt;br /&gt;
                 */&lt;br /&gt;
                error = (fwrite (buf, 1, nbytes, stream) != nbytes);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
        case IFFCMD_SEEK:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_SEEK asks that you performs a seek relative to the&lt;br /&gt;
                 * current position.  sc_NBytes is a signed number,&lt;br /&gt;
                 * indicating seek direction (positive for forward, negative&lt;br /&gt;
                 * for backward).  sc_Buf has no meaning here.&lt;br /&gt;
                 *&lt;br /&gt;
                 * Any error code returned will be remapped by IFFParse into&lt;br /&gt;
                 * IFFERR_SEEK.&lt;br /&gt;
                 */&lt;br /&gt;
                error = (fseek (stream, nbytes, 1) == -1);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
        case IFFCMD_INIT:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_INIT means to prepare your stream for reading.&lt;br /&gt;
                 * This is used for certain streams that can&#039;t be read&lt;br /&gt;
                 * immediately upon opening, and need further preparation.&lt;br /&gt;
                 * This operation is allowed to fail;  the error code placed&lt;br /&gt;
                 * in D0 will be returned directly to the client.&lt;br /&gt;
                 *&lt;br /&gt;
                 * An example of such a stream is the clipboard.  The&lt;br /&gt;
                 * clipboard.device requires you to set the io_ClipID and&lt;br /&gt;
                 * io_Offset to zero before starting a read.  You would&lt;br /&gt;
                 * perform such a sequence here.  (Stdio files need no such&lt;br /&gt;
                 * preparation, so we simply return zero for success.)&lt;br /&gt;
                 */&lt;br /&gt;
        case IFFCMD_CLEANUP:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_CLEANUP means to terminate the transaction with&lt;br /&gt;
                 * the associated stream.  This is used for streams that&lt;br /&gt;
                 * can&#039;t simply be closed.  This operation is not allowed to&lt;br /&gt;
                 * fail;  any error returned will be ignored.&lt;br /&gt;
                 *&lt;br /&gt;
                 * An example of such a stream is (surprise!) the clipboard.&lt;br /&gt;
                 * It requires you to explicitly end reads by CMD_READing&lt;br /&gt;
                 * past the end of a clip, and end writes by sending a&lt;br /&gt;
                 * CMD_UPDATE.  You would perform such a sequence here.&lt;br /&gt;
                 * (Again, stdio needs no such sequence.)&lt;br /&gt;
                 */&lt;br /&gt;
                error = 0;&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        return (error);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Custom Chunk Handlers ===&lt;br /&gt;
&lt;br /&gt;
Like custom stream handlers, custom chunk handlers are implemented using Hook structures. See the previous section for details on how a handler function may be interfaced using a Hook structure.&lt;br /&gt;
&lt;br /&gt;
There are two types of chunk handlers: entry handlers and exit handlers. Entry handlers are invoked just after the parser enters the chunk; the stream will be positioned to read the first byte in the chunk. (If the chunk is a FORM, LIST, CAT, or PROP, the longword type will be read by the parser; the stream will be positioned to read the byte immediately following the type.) Exit handlers are invoked just before the parser leaves the chunk; the stream is not positioned predictably within the chunk.&lt;br /&gt;
&lt;br /&gt;
==== Installing a Custom Chunk Handler ====&lt;br /&gt;
&lt;br /&gt;
To install an entry handler, you call EntryHandler() in the following manner:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;EntryHandler (iff, type, id, position, hookptr, object);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An exit handler is installed by saying:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;ExitHandler (iff, type, id, position, hookptr, object);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In both cases, a handler is installed for chunks having the specified type and id. The position argument specifies in what context to install the handler, and is identical to the position argument used by StoreLocalItem(). The hookptr argument given above is a pointer to your Hook structure.&lt;br /&gt;
&lt;br /&gt;
==== Inside a Custom Chunk Handler ====&lt;br /&gt;
&lt;br /&gt;
The mechanics of receiving parameters through the Hook are covered in the “Custom Stream Handlers” section, and are not duplicated here. Refer to the EntryHandler() and ExitHandler() Autodocs for the contents of the registers upon entry.&lt;br /&gt;
&lt;br /&gt;
Once inside your handler, you can call nearly all functions in the iffparse.library, however, you should avoid calling ParseIFF() from within chunk handlers.&lt;br /&gt;
&lt;br /&gt;
Your handler runs in the same environment as whoever called ParseIFF(). The propagation sequence is:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;mainline&#039;&#039;&#039; --&#039;&#039;calls&#039;&#039;--&amp;gt; &#039;&#039;&#039;ParseIFF()&#039;&#039;&#039; --&#039;&#039;calls&#039;&#039;--&amp;gt; &#039;&#039;&#039;your_handler()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Thus, your handler runs on your mainline’s stack, and can call any OS functions the mainline code can. (Your handler will have to set the global base pointer if your code uses base-relative addressing.)&lt;br /&gt;
&lt;br /&gt;
The return code will affect the parser in a number of ways:&lt;br /&gt;
&lt;br /&gt;
* If you return zero (a normal, uneventful return), ParseIFF() will continue normally. If you return the value IFFERR_RETURN2CLIENT, ParseIFF() will stop and return the value zero to the mainline code.&lt;br /&gt;
&lt;br /&gt;
* If you return any other value, ParseIFF() will stop and return that value to the mainline code. This is how you should return error conditions to the client code.&lt;br /&gt;
&lt;br /&gt;
==== The Object Parameter ====&lt;br /&gt;
&lt;br /&gt;
The object parameter supplied to EntryHandler() and ExitHandler() is a pointer which will be passed to your handler when invoked. This pointer can be anything; the parser doesn’t use it directly. As an example, you might pass the pointer to the IFFHandle. This way, when your handler is called, you’ll be able to easily perform operations on the IFFHandle within your handler. Such code might appear as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = EntryHandler (iff, ID_ILBM, ID_BMHD, IFFSLI_ROOT, hook, iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And your handler would be declared as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 MyHandler (struct Hook      *hook,&lt;br /&gt;
                 int32            *cmd,&lt;br /&gt;
                 struct IFFHandle *iff)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From within your handler, you could then call CurrentChunk(), ReadChunkBytes(), or nearly any other operation on the IFFHandle. Please refer to the EntryHandler() and ExitHandler() Autodocs for additional information on the use of chunk handlers.&lt;br /&gt;
&lt;br /&gt;
=== Finding the Prop Context ===&lt;br /&gt;
&lt;br /&gt;
Earlier it was mentioned that supplying a position value of IFFSLI_PROP to StoreLocalItem() would store it in the topmost property scope. FindPropContext() is the routine that finds that topmost context.&lt;br /&gt;
&lt;br /&gt;
Property chunks (such as the BMHD, CMAP, and others) have dominion over the FORM that contains them; they are said to be “in scope” and their definition persists until the FORM’s context ends. Thus, a property chunk has a scoping level equal to the FORM that contains it; when the FORM ends, the property dies with it.&lt;br /&gt;
&lt;br /&gt;
Consider a more complicated example. Suppose you have a LIST with a PROP in it. PROPs are the global variables of LISTs; thus a property chunk declared in a PROP will persist until the LIST’s context ends.&lt;br /&gt;
&lt;br /&gt;
This is what FindPropContext() is looking for; a context level in which a property chunk may be installed.&lt;br /&gt;
&lt;br /&gt;
FindPropContext() starts at the parent of the current context (second from the top of the context stack) and starts searching downward, looking for the first FORM or LIST context it finds. If it finds one, it returns a pointer to that ContextNode. If it can’t find a suitable context level, it returns NULL.&lt;br /&gt;
&lt;br /&gt;
FindPropContext() is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ContextNode *cn = IFFParse-&amp;gt;FindPropContext (iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Freeing LCIs ===&lt;br /&gt;
&lt;br /&gt;
Ordinarily, the parser will automatically delete LCIs you have allocated and installed. However, you may have a case where simply FreeVec()ing your LCI is not enough; you may need to free some ancillary memory, or decrement a counter, or send a signal, or something. This is where SetLocalItemPurge() comes in. It is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IFFParse-&amp;gt;SetLocalItemPurge (lci, hookptr);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the parser is ready to delete your LCI, your purge handler code will be called through the Hook you supplied. You can then perform all your necessary operations. One of these operations should be to free the LCI itself. This is done with FreeLocalItem():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IIFFParse-&amp;gt;FreeLocalItem (lci);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This deallocates the memory used to store the LCI and the client buffer allocated with it. FreeLocalItem() is only called as part of a custom purge handler.&lt;br /&gt;
&lt;br /&gt;
As with custom chunk handlers, your purge handler executes in the same environment as the mainline code that called ParseIFF(). It is recommended that you keep purge handlers short and to the point; super clever stuff should be reserved for custom chunk handlers, or for the client’s mainline code. Custom purge handlers must &#039;&#039;always&#039;&#039; work; failures will be ignored.&lt;br /&gt;
&lt;br /&gt;
== IFF FORM Specifications ==&lt;br /&gt;
&lt;br /&gt;
The specifications for Amiga IFF formats are maintained by the AmigaOS Development Team. The latest specifications are published in the [[IFF_Standard|IFF Standard]]. Updates of the IFF Manual, selected new FORMs and changes to existing FORMs are documented on this wiki.&lt;br /&gt;
&lt;br /&gt;
Some of the most commonly used IFF FORMs are the four that were originally specified in the EA IFF-85 standard:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| ILBM || Bitmap images and palettes&lt;br /&gt;
|-&lt;br /&gt;
| FTXT || Simple formatted text&lt;br /&gt;
|-&lt;br /&gt;
| SMUS || Simple musical scores&lt;br /&gt;
|-&lt;br /&gt;
| 8SVX || 8-bit sound samples&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Of these four, ILBM is the most commonly encountered FORM, and FTXT is becoming increasingly important since the &#039;&#039;conclip&#039;&#039; command passes clipped console text through the clipboard as FTXT. All data clipped to the clipboard must be in an IFF format.&lt;br /&gt;
&lt;br /&gt;
This section will provide a brief summary of the ILBM and FTXT FORMs and their most used common chunks. Please consult the EA-IFF specifications for additional information.&lt;br /&gt;
&lt;br /&gt;
=== FORM ILBM ===&lt;br /&gt;
&lt;br /&gt;
The IFF file format for graphic images on the Amiga is called FORM ILBM (InterLeaved BitMap). It follows a standard parsable IFF format.&lt;br /&gt;
&lt;br /&gt;
==== ILBM.BMHD BitMapHeader Chunk ====&lt;br /&gt;
&lt;br /&gt;
The most important chunk in a FORM ILBM is the BMHD (BitMapHeader) chunk which describes the size and compression of the image:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
typedef UBYTE Masking;  /* Choice of masking technique - Usually 0. */&lt;br /&gt;
#define mskNone                 0&lt;br /&gt;
#define mskHasMask              1&lt;br /&gt;
#define mskHasTransparentColor  2&lt;br /&gt;
#define mskLasso                3&lt;br /&gt;
&lt;br /&gt;
/* Compression algorithm applied to the rows of all source&lt;br /&gt;
 * and mask planes. &amp;amp;quot;cmpByteRun1&amp;amp;quot; is byte run encoding.&lt;br /&gt;
 * Do not compress across rows!  Compression is usually 1.&lt;br /&gt;
 */&lt;br /&gt;
typedef UBYTE Compression;&lt;br /&gt;
#define cmpNone                 0&lt;br /&gt;
#define cmpByteRun1             1&lt;br /&gt;
&lt;br /&gt;
/* The BitMapHeader structure expressed as a C structure */&lt;br /&gt;
typedef struct {&lt;br /&gt;
        UWORD w, h;             /* raster width &amp;amp;amp; height in pixels      */&lt;br /&gt;
        WORD  x, y;             /* pixel position for this image        */&lt;br /&gt;
        UBYTE nPlanes;          /* # bitplanes (without mask, if any)   */&lt;br /&gt;
        Masking     masking;    /* One of the values above.  Usually 0  */&lt;br /&gt;
        Compression compression;/* One of the values above.  Usually 1  */&lt;br /&gt;
        UBYTE reserved1;        /* reserved; ignore on read, write as 0 */&lt;br /&gt;
        UWORD transparentColor; /* transparent color number. Usually 0  */&lt;br /&gt;
        UBYTE xAspect, yAspect; /* pixel aspect, a ratio width : height */&lt;br /&gt;
        WORD  pageWidth, pageHeight;    /* source &amp;amp;quot;page&amp;amp;quot; size in pixels */&lt;br /&gt;
} BitMapHeader;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=This hex dump is shown only to help the reader understand how IFF chunks appear in a file. You cannot ever depend on any particular ILBM chunk being at any particular offset into the file. IFF files are composed, in their simplest form, of chunks within a FORM. Each chunk starts with a 4-letter chunkID, followed by a 32-bit length of the rest of the chunk. You must &#039;&#039;parse&#039;&#039; IFF files, skipping past unneeded or unknown chunks by seeking their length (+1 if odd length) to the next 4-letter chunk ID. In a real ILBM file, you are likely to encounter additional optional chunks. See the IFF Specification listed in [[IFF_Standard|IFF Standard]] for additional information on such chunks.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0000: 464F524D 00016418 494C424D 424D4844    FORM..d.ILBMBMHD&lt;br /&gt;
0010: 00000014 01400190 00000000 06000100    .....@..........&lt;br /&gt;
0020: 00000A0B 01400190 43414D47 00000004    .....@..CAMG....&lt;br /&gt;
0030: 00000804 434D4150 00000030 001000E0    ....CMAP...0....&lt;br /&gt;
0040: E0E00000 20000050 30303050 50500030    .... ..P000PPP.0&lt;br /&gt;
0050: 90805040 70707010 60E02060 E06080D0    ..P@ppp.`. `.`..&lt;br /&gt;
0060: A0A0A0A0 90E0C0C0 C0D0A0E0 424F4459    ............BODY&lt;br /&gt;
0070: 000163AC F8000F80 148A5544 2ABDEFFF    ..c.......UD*...  etc.&lt;br /&gt;
&lt;br /&gt;
Interpretation:&lt;br /&gt;
&lt;br /&gt;
      &#039;F O R M&#039; length  &#039;I L B M&#039;&#039;B M H D&#039;&amp;amp;lt;-start of BitMapHeader chunk&lt;br /&gt;
0000: 464F524D 00016418 494C424D 424D4844    FORM..d.ILBMBMHD&lt;br /&gt;
&lt;br /&gt;
       length  WideHigh XorgYorg PlMkCoRe &amp;amp;lt;- Planes Mask Compression Reserved&lt;br /&gt;
0010: 00000014 01400190 00000000 06000100    .....@..........&lt;br /&gt;
&lt;br /&gt;
      TranAspt PagwPagh &#039;C A M G&#039; length  &amp;amp;lt;- start of C-AMiGa View modes chunk&lt;br /&gt;
0020: 00000A0B 01400190 43414D47 00000004    .....@..CAMG....&lt;br /&gt;
&lt;br /&gt;
dir include:&lt;br /&gt;
      ViewMode &#039;C M A P&#039; length   R g b R &amp;amp;lt;- ViewMode 800=HAM | 4=LACE&lt;br /&gt;
0030: 00000804 434D4150 00000030 001000E0    ....CMAP...0....&lt;br /&gt;
&lt;br /&gt;
       g b R g  b R g b  R g b R  g b R g &amp;amp;lt;- Rgb&#039;s are for reg0 thru regN&lt;br /&gt;
0040: E0E00000 20000050 30303050 50500030    .... ..P000PPP.0&lt;br /&gt;
&lt;br /&gt;
       b R g b  R g b R  g b R g  b R g b&lt;br /&gt;
0050: 90805040 70707010 60E02060 E06080D0    ..P@ppp.`. `.`..&lt;br /&gt;
&lt;br /&gt;
       R g b R  g b R g  b R g b &#039;B O D Y&#039;&lt;br /&gt;
0060: A0A0A0A0 90E0C0C0 C0D0A000 424F4459    ............BODY&lt;br /&gt;
&lt;br /&gt;
       length   start of body data        &amp;amp;lt;- Compacted (Compression=1 above)&lt;br /&gt;
0070: 000163AC F8000F80 148A5544 2ABDEFFF    ..c.......UD*...&lt;br /&gt;
0080: FFBFF800 0F7FF7FC FF04F85A 77AD5DFE    ...........Zw.].  etc.&lt;br /&gt;
&lt;br /&gt;
Simple CAMG ViewModes:  HIRES=0x8000  LACE=0x4  HAM=0x800  HALFBRITE=0x80&lt;br /&gt;
&lt;br /&gt;
( ILBMs may contain a LONGWORD ViewPort ModeID in CAMG )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Interpreting ILBMs ====&lt;br /&gt;
&lt;br /&gt;
ILBM is a fairly simple IFF FORM. All you really need to deal with to extract the image are the following chunks:&lt;br /&gt;
&lt;br /&gt;
Information about the size, depth, compaction method (see interpreted hex dump above).&lt;br /&gt;
&lt;br /&gt;
Optional Amiga ViewModes chunk. Most HAM and HALFBRITE ILBMs should have this chunk. If no CAMG chunk is present, and image is 6 planes deep, assume HAM and you’ll probably be right. Some Amiga ViewModes flags are HIRES=0x8000, LACE=0x4, HAM=0x800, HALFBRITE=0x80. 2.0 ILBM writers should write a full 32-bit mode ID in the CAMG. See the IFF Manual for more information on writing and interpreting 32-bit CAMG values.&lt;br /&gt;
&lt;br /&gt;
RGB values for color registers 0 to N. Previously, 4-bit RGB components each left justified in a byte. These should now be stored as a full 8-bit RGB values by duplicating 4-bit values in the high and low nibble of the byte.&lt;br /&gt;
&lt;br /&gt;
The pixel data, stored in an interleaved fashion as follows (each line individually compacted if BMHD Compression = 1):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
plane 0 scan line 0&lt;br /&gt;
plane 1 scan line 0&lt;br /&gt;
plane 2 scan line 0&lt;br /&gt;
...&lt;br /&gt;
plane n scan line 0&lt;br /&gt;
plane 0 scan line 1&lt;br /&gt;
plane 1 scan line 1&lt;br /&gt;
etc.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also watch for AUTH Author chunks and (c) copyright chunks and preserve any copyright information if you rewrite the ILBM.&lt;br /&gt;
&lt;br /&gt;
==== ILBM BODY Compression ====&lt;br /&gt;
&lt;br /&gt;
The BODY contains pixel data for the image. Width, Height, and depth (Planes) is specified in the BMHD.&lt;br /&gt;
&lt;br /&gt;
If the BMHD Compression byte is 0, then the scan line data is not compressed. If Compression=1, then each scan line is individually compressed as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;while (not produced the desired number of bytes)&lt;br /&gt;
&lt;br /&gt;
    /* get a byte, call it N */&lt;br /&gt;
&lt;br /&gt;
    if (N &amp;amp;gt;= 0 &amp;amp;amp;&amp;amp;amp; N &amp;amp;lt;= 127)&lt;br /&gt;
        /* copy the next N+1 bytes literally */&lt;br /&gt;
&lt;br /&gt;
    if (N &amp;amp;gt;= -127 &amp;amp;amp;&amp;amp;amp; N &amp;amp;lt;= -1)&lt;br /&gt;
        /* repeat the next byte N+1 times */&lt;br /&gt;
&lt;br /&gt;
    if (N == -128)&lt;br /&gt;
        /* skip it, presumably it&#039;s padding */&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== Interpreting the Scan Line Data ====&lt;br /&gt;
&lt;br /&gt;
If the ILBM is not HAM or HALFBRITE, then after parsing and uncompacting if necessary, you will have N planes of pixel data. Color register used for each pixel is specified by looking at each pixel thru the planes. For instance, if you have 5 planes, and the bit for a particular pixel is set in planes 0 and 3:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PLANE     4 3 2 1 0&lt;br /&gt;
PIXEL     0 1 0 0 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
then that pixel uses color register binary 01001 = 9.&lt;br /&gt;
&lt;br /&gt;
The RGB value for each color register is stored in the CMAP chunk of the ILBM, starting with register 0, with each register’s RGB value stored as one byte of R, one byte G, and one byte of B, with each component left justified in the byte. (i.e. Amiga R, G, and B components are each stored in the high nibble of a byte)&lt;br /&gt;
&lt;br /&gt;
But, if the picture is HAM or HALFBRITE, it is interpreted differently. Hopefully, if the picture is HAM or HALFBRITE, the package that saved it properly saved a CAMG chunk (look at a hex dump of your file with ASCII interpretation - you will see the chunks - they all start with a 4-ASCII-char chunk ID). If the picture is 6 planes deep and has no CAMG chunk, it is probably HAM. If you see a CAMG chunk, the ‘CAMG’ is followed by the 32-bit chunk length, and then the 32-bit Amiga view mode flags.&lt;br /&gt;
&lt;br /&gt;
HAM pictures will have the 0x800 bit set in CAMG chunk ViewModes. HALFBRITE pictures will have the 0x80 bit set. See the graphics library articles for more information on HAM and HALFBRITE modes.&lt;br /&gt;
&lt;br /&gt;
==== Other ILBM Notes ====&lt;br /&gt;
&lt;br /&gt;
Amiga ILBMs images must be stored as an even number of bytes in width. However, the ILBM BMHD field w (width) should describe the actual image width, not the rounded up width as stored.&lt;br /&gt;
&lt;br /&gt;
ILBMs created with Electronic Arts IBM or Amiga &#039;&#039;Deluxe Paint II&#039;&#039; packages are compatible (though you may have to use a ‘.lbm’ filename extension on an IBM). The ILBM graphic files may be transferred between the machines (or between the Amiga and IBM sides your Amiga if you have a CBM Bridgeboard card installed) and loaded into either package.&lt;br /&gt;
&lt;br /&gt;
=== FORM FTXT ===&lt;br /&gt;
&lt;br /&gt;
The FTXT (Formatted TeXT) form is the standard format for sharing text on the Amiga. The console device clip and paste functions, in conjunction with the startup-sequence &#039;&#039;conclip&#039;&#039; command, pass clipped console text through the clipboard as FTXT.&lt;br /&gt;
&lt;br /&gt;
By supporting reading and writing of clipboard device FTXT, your application will allow users to cut and paste text between your application, other applications, and Amiga Shell windows.&lt;br /&gt;
&lt;br /&gt;
The FTXT form is very simple. Generally, for clip and paste operations, you will only be concerned with the CHRS (character string) chunks of an FTXT. There may be one or more CHRS chunks, each of which contains a non-NULL-terminated string of ASCII characters whose length is equal to the length (StoredProperty.sp_Size) of the chunk.&lt;br /&gt;
&lt;br /&gt;
Be aware that if you CollectionChunk() the CHRS chunks of an FTXT, the list accumulated by IFFParse will be in reverse order from the chunk order in the file. See the ClipFTXT.c example at the end of this article for a simple example of reading (and optionally writing) FTXT to and from the clipboard. See also the [[Clipboard_Device|Clipboard Device]] and [[Console_Device|Console Device]] for more information on console clip and paste.&lt;br /&gt;
&lt;br /&gt;
== IFFParse Examples ==&lt;br /&gt;
&lt;br /&gt;
Two examples follow: “ClipFTXT.c” and “Sift.c”. These are simple examples that demonstrate the use of the IFFParse library. More complex examples for displaying and saving ILBMs may be found in the [[IFF_Standard|IFF Standard]] section.&lt;br /&gt;
&lt;br /&gt;
=== Clipboard FTXT Example ===&lt;br /&gt;
&lt;br /&gt;
ClipFTXT.c demonstrates reading (and optional writing) of clipboard device FTXT. This example may be used as the basis for supporting console pastes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 *&lt;br /&gt;
 * clipftxt.c:   Writes ASCII text to clipboard unit as FTXT&lt;br /&gt;
 *               (All clipboard data must be IFF)&lt;br /&gt;
 *&lt;br /&gt;
 * Usage: clipftxt unitnumber&lt;br /&gt;
 *&lt;br /&gt;
 * To convert to an example of reading only, comment out #define WRITEREAD&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Causes example to write FTXT first, then read it back&lt;br /&gt;
 * Comment out to create a reader only&lt;br /&gt;
 */&lt;br /&gt;
#define WRITEREAD&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define MINARGS 2&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR usage = &amp;quot;Usage: clipftxt unitnumber (use zero for primary unit)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Text error messages for possible IFFERR_#? returns from various&lt;br /&gt;
 * IFF routines.  To get the index into this array, take your IFFERR code,&lt;br /&gt;
 * negate it, and subtract one.&lt;br /&gt;
 *  idx = -error - 1;&lt;br /&gt;
 */&lt;br /&gt;
char *errormsgs[] = {&lt;br /&gt;
     &amp;quot;End of file (not an error).&amp;quot;,&lt;br /&gt;
     &amp;quot;End of context (not an error).&amp;quot;,&lt;br /&gt;
     &amp;quot;No lexical scope.&amp;quot;,&lt;br /&gt;
     &amp;quot;Insufficient memory.&amp;quot;,&lt;br /&gt;
     &amp;quot;Stream read error.&amp;quot;,&lt;br /&gt;
     &amp;quot;Stream write error.&amp;quot;,&lt;br /&gt;
     &amp;quot;Stream seek error.&amp;quot;,&lt;br /&gt;
     &amp;quot;File is corrupt.&amp;quot;,&lt;br /&gt;
     &amp;quot;IFF syntax error.&amp;quot;,&lt;br /&gt;
     &amp;quot;Not an IFF file.&amp;quot;,&lt;br /&gt;
     &amp;quot;Required call-back hook missing.&amp;quot;,&lt;br /&gt;
     &amp;quot;Return to client.  You should never see this.&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define RBUFSZ 512&lt;br /&gt;
&lt;br /&gt;
#define  ID_FTXT        MAKE_ID(&#039;F&#039;,&#039;T&#039;,&#039;X&#039;,&#039;T&#039;)&lt;br /&gt;
#define  ID_CHRS        MAKE_ID(&#039;C&#039;,&#039;H&#039;,&#039;R&#039;,&#039;S&#039;)&lt;br /&gt;
&lt;br /&gt;
struct IFFParseIFace *IIFFParse;&lt;br /&gt;
&lt;br /&gt;
UBYTE mytext[]=&amp;quot;This FTXT written to clipboard by clipftxt example.\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
        struct IFFHandle   *iff = NULL;&lt;br /&gt;
        struct ContextNode *cn;&lt;br /&gt;
        long                error=0, unitnumber=0, rlen;&lt;br /&gt;
        int                 textlen;&lt;br /&gt;
        UBYTE               readbuf[RBUFSZ];&lt;br /&gt;
&lt;br /&gt;
        /* if not enough args or &#039;?&#039;, print usage */&lt;br /&gt;
        if(((argc)&amp;amp;&amp;amp;(argc&amp;lt;MINARGS))||(argv[argc-1][0]==&#039;?&#039;))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;%s\n&amp;quot;, usage);&lt;br /&gt;
                return RETURN_WARN;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        unitnumber = atoi(argv[1]);&lt;br /&gt;
&lt;br /&gt;
        struct Library *IFFParseBase = IExec-&amp;gt;OpenLibrary(&amp;quot;iffparse.library&amp;quot;, 50);&lt;br /&gt;
        IIFFParse = (struct IFFParseIFace*)IExec-&amp;gt;GetInterface(IFFParseBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
        &lt;br /&gt;
        if (IIFFParse == NULL)&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open iff parsing library.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Allocate IFF_File structure.&lt;br /&gt;
         */&lt;br /&gt;
        if (!(iff = IIFFParse-&amp;gt;AllocIFF ()))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;AllocIFF() failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Set up IFF_File for Clipboard I/O.&lt;br /&gt;
         */&lt;br /&gt;
        if (!(iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard (unitnumber)))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Clipboard open failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Opened clipboard unit %ld\n&amp;quot;, unitnumber);&lt;br /&gt;
&lt;br /&gt;
        IIFFParse-&amp;gt;InitIFFasClip (iff);&lt;br /&gt;
&lt;br /&gt;
#ifdef WRITEREAD&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Start the IFF transaction.&lt;br /&gt;
         */&lt;br /&gt;
        if (error = IIFFParse-&amp;gt;OpenIFF (iff, IFFF_WRITE))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;OpenIFF for write failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Write our text to the clipboard as CHRS chunk in FORM FTXT&lt;br /&gt;
         *&lt;br /&gt;
         * First, write the FORM ID (FTXT)&lt;br /&gt;
         */&lt;br /&gt;
        if(!(error = IIFFParse-&amp;gt;PushChunk(iff, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN)))&lt;br /&gt;
        {&lt;br /&gt;
                /* Now the CHRS chunk ID followed by the chunk data&lt;br /&gt;
                 * We&#039;ll just write one CHRS chunk.&lt;br /&gt;
                 * You could write more chunks.&lt;br /&gt;
                 */&lt;br /&gt;
                if(!(error = IIFFParse-&amp;gt;PushChunk(iff, 0, ID_CHRS, IFFSIZE_UNKNOWN)))&lt;br /&gt;
                {&lt;br /&gt;
                        /* Now the actual data (the text) */&lt;br /&gt;
                        textlen = strlen(mytext);&lt;br /&gt;
                        if(IIFFParse-&amp;gt;WriteChunkBytes(iff, mytext, textlen) != textlen)&lt;br /&gt;
                        {&lt;br /&gt;
                                IDOS-&amp;gt;Printf(&amp;quot;Error writing CHRS data.\n&amp;quot;);&lt;br /&gt;
                                error = IFFERR_WRITE;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
                if(!error) error = IIFFParse-&amp;gt;PopChunk(iff);&lt;br /&gt;
        }&lt;br /&gt;
        if(!error) error = IIFFParse-&amp;gt;PopChunk(iff);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        if(error)&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;IFF write failed, error %ld: %s\n&amp;quot;,&lt;br /&gt;
                             error, errormsgs[-error - 1]);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Wrote text to clipboard as FTXT\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Now let&#039;s close it, then read it back&lt;br /&gt;
         * First close the write handle, then close the clipboard&lt;br /&gt;
         */&lt;br /&gt;
        IIFFParse-&amp;gt;CloseIFF(iff);&lt;br /&gt;
        if (iff-&amp;gt;iff_Stream) IIFFParse-&amp;gt;CloseClipboard ((struct ClipboardHandle *)&lt;br /&gt;
                                                        iff-&amp;gt;iff_Stream);&lt;br /&gt;
&lt;br /&gt;
        if (!(iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard (unitnumber)))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Reopen of Clipboard failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Reopened clipboard unit %ld\n&amp;quot;, unitnumber);&lt;br /&gt;
&lt;br /&gt;
#endif /* WRITEREAD */&lt;br /&gt;
&lt;br /&gt;
        if (error = IIFFParse-&amp;gt;OpenIFF (iff, IFFF_READ))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;OpenIFF for read failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Tell iffparse we want to stop on FTXT CHRS chunks */&lt;br /&gt;
        if (error = IFFParse-&amp;gt;StopChunk(iff, ID_FTXT, ID_CHRS))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;StopChunk failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Find all of the FTXT CHRS chunks */&lt;br /&gt;
        while(1)&lt;br /&gt;
        {&lt;br /&gt;
                error = IIFFParse-&amp;gt;ParseIFF(iff,IFFPARSE_SCAN);&lt;br /&gt;
                if(error == IFFERR_EOC) continue;       /* enter next context */&lt;br /&gt;
                else if(error) break;&lt;br /&gt;
&lt;br /&gt;
                /* We only asked to stop at FTXT CHRS chunks&lt;br /&gt;
                 * If no error we&#039;ve hit a stop chunk&lt;br /&gt;
                 * Read the CHRS chunk data&lt;br /&gt;
                 */&lt;br /&gt;
                cn = IIFFParse-&amp;gt;CurrentChunk(iff);&lt;br /&gt;
&lt;br /&gt;
                if((cn)&amp;amp;&amp;amp;(cn-&amp;gt;cn_Type == ID_FTXT)&amp;amp;&amp;amp;(cn-&amp;gt;cn_ID == ID_CHRS))&lt;br /&gt;
                {&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;CHRS chunk contains:\n&amp;quot;);&lt;br /&gt;
                        while((rlen = IIFFParse-&amp;gt;ReadChunkBytes(iff,readbuf,RBUFSZ)) &amp;gt; 0)&lt;br /&gt;
                        {&lt;br /&gt;
                                IDOS-&amp;gt;Write(Output(),readbuf,rlen);&lt;br /&gt;
                        }&lt;br /&gt;
                        if(rlen &amp;lt; 0)    error = rlen;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if((error)&amp;amp;&amp;amp;(error != IFFERR_EOF))&lt;br /&gt;
        {&lt;br /&gt;
                IDOS-&amp;gt;Printf (&amp;quot;IFF read failed, error %ld: %s\n&amp;quot;,&lt;br /&gt;
                              error, errormsgs[-error - 1]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
bye:&lt;br /&gt;
        if (iff)&lt;br /&gt;
        {&lt;br /&gt;
                /*&lt;br /&gt;
                 * Terminate the IFF transaction with the stream.  Free&lt;br /&gt;
                 * all associated structures.&lt;br /&gt;
                 */&lt;br /&gt;
                IIFFParse-&amp;gt;CloseIFF (iff);&lt;br /&gt;
&lt;br /&gt;
                /*&lt;br /&gt;
                 * Close the clipboard stream&lt;br /&gt;
                 */&lt;br /&gt;
                if (iff-&amp;gt;iff_Stream)&lt;br /&gt;
                        IIFFParse-&amp;gt;CloseClipboard ((struct ClipboardHandle *)&lt;br /&gt;
                                                   iff-&amp;gt;iff_Stream);&lt;br /&gt;
                /*&lt;br /&gt;
                 * Free the IFF_File structure itself.&lt;br /&gt;
                 */&lt;br /&gt;
                IIFFParse-&amp;gt;FreeIFF (iff);&lt;br /&gt;
        }&lt;br /&gt;
        IExec-&amp;gt;DropInterface((struct Interface*)IIFFParse);&lt;br /&gt;
        IExec-&amp;gt;CloseLibrary (IFFParseBase);&lt;br /&gt;
&lt;br /&gt;
        return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== IFF Scanner Example ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Sift.c&amp;quot; lists the type and size of every chunk in an IFF file and, and checks the IFF file for correct syntax. You should use &amp;quot;Sift&amp;quot; to check IFF files created by your programs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 *&lt;br /&gt;
 * sift.c:       Takes any IFF file and tells you what&#039;s in it.  Verifies syntax and all that cool stuff.&lt;br /&gt;
 *&lt;br /&gt;
 * Usage: sift -c          ; For clipboard scanning&lt;br /&gt;
 *    or  sift &amp;lt;file&amp;gt;      ; For DOS file scanning&lt;br /&gt;
 *&lt;br /&gt;
 * Reads the specified stream and prints an IFFCheck-like listing of the contents of the IFF file, if any.&lt;br /&gt;
 * Stream is a DOS file for &amp;lt;file&amp;gt; argument, or is the clipboard&#039;s primary clip for -c.&lt;br /&gt;
 * This program must be run from a CLI.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MINARGS 2&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR usage = &amp;quot;Usage: sift IFFfilename (or -c for clipboard)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void PrintTopChunk(struct IFFHandle *);  /* prototype for our function */&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Text error messages for possible IFFERR_#? returns from various IFF routines.  To get the index into&lt;br /&gt;
 * this array, take your IFFERR code, negate it, and subtract one.&lt;br /&gt;
 *  idx = -error - 1;&lt;br /&gt;
 */&lt;br /&gt;
char    *errormsgs[] = {&lt;br /&gt;
        &amp;quot;End of file (not an error).&amp;quot;, &amp;quot;End of context (not an error).&amp;quot;, &amp;quot;No lexical scope.&amp;quot;,&lt;br /&gt;
        &amp;quot;Insufficient memory.&amp;quot;, &amp;quot;Stream read error.&amp;quot;, &amp;quot;Stream write error.&amp;quot;,&lt;br /&gt;
        &amp;quot;Stream seek error.&amp;quot;, &amp;quot;File is corrupt.&amp;quot;, &amp;quot;IFF syntax error.&amp;quot;,&lt;br /&gt;
        &amp;quot;Not an IFF file.&amp;quot;, &amp;quot;Required call-back hook missing.&amp;quot;, &amp;quot;Return to client.  You should never see this.&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct IFFParseIFace *IIFFParse = NULL;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    struct IFFHandle    *iff = NULL;&lt;br /&gt;
    int32                error;&lt;br /&gt;
    int16                cbio;&lt;br /&gt;
&lt;br /&gt;
        /* if not enough args or &#039;?&#039;, print usage */&lt;br /&gt;
        if(((argc)&amp;amp;&amp;amp;(argc&amp;lt;MINARGS))||(argv[argc-1][0]==&#039;?&#039;))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;%s\n&amp;quot;, usage);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Check to see if we are doing I/O to the Clipboard. */&lt;br /&gt;
        cbio = (argv[1][0] == &#039;-&#039;  &amp;amp;&amp;amp;  argv[1][1] == &#039;c&#039;);&lt;br /&gt;
&lt;br /&gt;
        struct Library *IFFParseBase = IExec-&amp;gt;OpenLibrary(&amp;quot;iffparse.library&amp;quot;, 50);&lt;br /&gt;
        IIFFParse = (struct IFFParseIFace*)IExec-&amp;gt;GetInterface(IFFParseBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
        if (IIFFParse == NULL)&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open iff parsing library.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Allocate IFF_File structure. */&lt;br /&gt;
        if (!(iff = IIFFParse-&amp;gt;AllocIFF()))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;AllocIFF() failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Internal support is provided for both AmigaDOS files, and the clipboard.device. This bizarre&lt;br /&gt;
         * &#039;if&#039; statement performs the appropriate machinations for each case.&lt;br /&gt;
         */&lt;br /&gt;
        if (cbio)&lt;br /&gt;
                {&lt;br /&gt;
                /*&lt;br /&gt;
                 * Set up IFF_File for Clipboard I/O.&lt;br /&gt;
                 */&lt;br /&gt;
                if (!(iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard(PRIMARY_CLIP)))&lt;br /&gt;
                        {&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;Clipboard open failed.\n&amp;quot;);&lt;br /&gt;
                        goto bye;&lt;br /&gt;
                        }&lt;br /&gt;
                IIFFParse-&amp;gt;InitIFFasClip(iff);&lt;br /&gt;
                }&lt;br /&gt;
        else&lt;br /&gt;
                {&lt;br /&gt;
                /* Set up IFF_File for AmigaDOS I/O.  */&lt;br /&gt;
                if (!(iff-&amp;gt;iff_Stream = IDOS-&amp;gt;Open(argv[1], MODE_OLDFILE)))&lt;br /&gt;
                        {&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;File open failed.\n&amp;quot;);&lt;br /&gt;
                        goto bye;&lt;br /&gt;
                        }&lt;br /&gt;
                IIFFParse-&amp;gt;InitIFFasDOS(iff);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Start the IFF transaction. */&lt;br /&gt;
        if (error = IIFFParse-&amp;gt;OpenIFF(iff, IFFF_READ))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;OpenIFF failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        while (1)&lt;br /&gt;
                {&lt;br /&gt;
                /*&lt;br /&gt;
                 * The interesting bit. IFFPARSE_RAWSTEP permits us to have precision monitoring of the&lt;br /&gt;
                 * parsing process, which is necessary if we wish to print the structure of an IFF file.&lt;br /&gt;
                 * ParseIFF() with _RAWSTEP will return the following things for the following reasons:&lt;br /&gt;
                 *&lt;br /&gt;
                 * Return code:                 Reason:&lt;br /&gt;
                 * 0                            Entered new context.&lt;br /&gt;
                 * IFFERR_EOC                   About to leave a context.&lt;br /&gt;
                 * IFFERR_EOF                   Encountered end-of-file.&lt;br /&gt;
                 * &amp;lt;anything else&amp;gt;              A parsing error.&lt;br /&gt;
                 */&lt;br /&gt;
                error = IIFFParse-&amp;gt;ParseIFF(iff, IFFPARSE_RAWSTEP);&lt;br /&gt;
&lt;br /&gt;
                /*&lt;br /&gt;
                 * Since we&#039;re only interested in when we enter a context, we &amp;quot;discard&amp;quot; end-of-context&lt;br /&gt;
                 * (_EOC) events.&lt;br /&gt;
                 */&lt;br /&gt;
                if (error == IFFERR_EOC)&lt;br /&gt;
                        continue;&lt;br /&gt;
                else if (error)&lt;br /&gt;
                        /*&lt;br /&gt;
                         * Leave the loop if there is any other error.&lt;br /&gt;
                         */&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                /* If we get here, error was zero. Print out the current state of affairs. */&lt;br /&gt;
                PrintTopChunk(iff);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * If error was IFFERR_EOF, then the parser encountered the end of&lt;br /&gt;
         * the file without problems. Otherwise, we print a diagnostic.&lt;br /&gt;
         */&lt;br /&gt;
        if (error == IFFERR_EOF)&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;File scan complete.\n&amp;quot;);&lt;br /&gt;
        else&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;File scan aborted, error %ld: %s\n&amp;quot;,&lt;br /&gt;
                        error, errormsgs[-error - 1]);&lt;br /&gt;
&lt;br /&gt;
bye:&lt;br /&gt;
        if (iff) {&lt;br /&gt;
                /* Terminate the IFF transaction with the stream. Free all associated structures. */&lt;br /&gt;
                IIFFParse-&amp;gt;CloseIFF(iff);&lt;br /&gt;
&lt;br /&gt;
                /*&lt;br /&gt;
                 * Close the stream itself.&lt;br /&gt;
                 */&lt;br /&gt;
                if (iff-&amp;gt;iff_Stream)&lt;br /&gt;
                        if (cbio)&lt;br /&gt;
                                IIFFParse-&amp;gt;CloseClipboard((struct ClipboardHandle *)iff-&amp;gt;iff_Stream);&lt;br /&gt;
                        else&lt;br /&gt;
                                IDOS-&amp;gt;Close(iff-&amp;gt;iff_Stream);&lt;br /&gt;
&lt;br /&gt;
                /* Free the IFF_File structure itself. */&lt;br /&gt;
                IIFFParse-&amp;gt;FreeIFF(iff);&lt;br /&gt;
                }&lt;br /&gt;
        IExec-&amp;gt;DropInterface((struct Interface*)IIFFParse);&lt;br /&gt;
        IExec-&amp;gt;CloseLibrary(IFFParseBase);&lt;br /&gt;
&lt;br /&gt;
        return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void PrintTopChunk(struct IFFHandle *iff)&lt;br /&gt;
{&lt;br /&gt;
        struct ContextNode      *top;&lt;br /&gt;
        int32                   i;&lt;br /&gt;
        char                    idbuf[5];&lt;br /&gt;
&lt;br /&gt;
        /* Get a pointer to the context node describing the current context. */&lt;br /&gt;
        if (!(top = IIFFParse-&amp;gt;CurrentChunk(iff)))&lt;br /&gt;
                return;&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Print a series of dots equivalent to the current nesting depth of chunks processed so far.&lt;br /&gt;
         * This will cause nested chunks to be printed out indented.&lt;br /&gt;
         */&lt;br /&gt;
        for (i = iff-&amp;gt;iff_Depth;  i--; )&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;. &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        /* Print out the current chunk&#039;s ID and size. */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;%s %ld &amp;quot;, IIFFParse-&amp;gt;IDtoStr(top-&amp;gt;cn_ID, idbuf), top-&amp;gt;cn_Size);&lt;br /&gt;
&lt;br /&gt;
        /* Print the current chunk&#039;s type, with a newline. */&lt;br /&gt;
        IDOS-&amp;gt;Printf(IIFFParse-&amp;gt;IDtoStr(top-&amp;gt;cn_Type, idbuf));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the IFFParse functions discussed in this article. Further information about these and other IFFParse functions can be found in the SDK.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AllocIFF()&lt;br /&gt;
| Creates an IFFHandle structure.&lt;br /&gt;
|-&lt;br /&gt;
| FreeIFF()&lt;br /&gt;
| Frees the IFFHandle structure created with AllocIFF().&lt;br /&gt;
|-&lt;br /&gt;
| OpenIFF()&lt;br /&gt;
| Initialize an IFFHandle structure to read or write an IFF stream.&lt;br /&gt;
|-&lt;br /&gt;
| CloseIFF()&lt;br /&gt;
| Closes an IFF context.&lt;br /&gt;
|-&lt;br /&gt;
| InitIFF()&lt;br /&gt;
| Initialize an IFFHandle as a user-defined stream.&lt;br /&gt;
|-&lt;br /&gt;
| InitIFFasDOS()&lt;br /&gt;
| Initialize an IFFHandle as an AmigaDOS stream.&lt;br /&gt;
|-&lt;br /&gt;
| InitIFFasClip()&lt;br /&gt;
| Initialize an IFFHandle as a clipboard stream.&lt;br /&gt;
|-&lt;br /&gt;
| OpenClipboard()&lt;br /&gt;
| Create a handle on a clipboard unit for InitIFFasClip().&lt;br /&gt;
|-&lt;br /&gt;
| ParseIFF()&lt;br /&gt;
| Parse an IFF file from an IFFHandle stream.&lt;br /&gt;
|-&lt;br /&gt;
| ReadChunkBytes()&lt;br /&gt;
| Read bytes from the current chunk into a buffer.&lt;br /&gt;
|-&lt;br /&gt;
| ReadChunkRecords()&lt;br /&gt;
| Read record elements from the current chunk into a buffer.&lt;br /&gt;
|-&lt;br /&gt;
| StopChunk()&lt;br /&gt;
| Declare a chunk that should cause ParseIFF() to return.&lt;br /&gt;
|-&lt;br /&gt;
| CurrentChunk()&lt;br /&gt;
| Get the context node for the current chunk.&lt;br /&gt;
|-&lt;br /&gt;
| PropChunk()&lt;br /&gt;
| Specify a property chunk to store.&lt;br /&gt;
|-&lt;br /&gt;
| FindProp()&lt;br /&gt;
| Search for a stored property in a given context.&lt;br /&gt;
|-&lt;br /&gt;
| CollectionChunk()&lt;br /&gt;
| Declare a chunk type for collection.&lt;br /&gt;
|-&lt;br /&gt;
| FindCollection()&lt;br /&gt;
| Get a pointer to the current list of collection items.&lt;br /&gt;
|-&lt;br /&gt;
| StopOnExit()&lt;br /&gt;
| Declare a stop condition for exiting a chunk.&lt;br /&gt;
|-&lt;br /&gt;
| EntryHandler()&lt;br /&gt;
| Add an entry handler to the IFFHandle context.&lt;br /&gt;
|-&lt;br /&gt;
| ExitHandler()&lt;br /&gt;
| Add an exit handler to the IFFHandle context.&lt;br /&gt;
|-&lt;br /&gt;
| PushChunk()&lt;br /&gt;
| Push a given context node onto the top of the context stack.&lt;br /&gt;
|-&lt;br /&gt;
| PopChunk()&lt;br /&gt;
| Pop the top context node off of the context stack.&lt;br /&gt;
|-&lt;br /&gt;
| CurrentChunk()&lt;br /&gt;
| Get the top context node for the current chunk.&lt;br /&gt;
|-&lt;br /&gt;
| ParentChunk()&lt;br /&gt;
| Get the nesting context node for a given chunk.&lt;br /&gt;
|-&lt;br /&gt;
| AllocLocalItem()&lt;br /&gt;
| Create a LocalContextItem (LCI) structure.&lt;br /&gt;
|-&lt;br /&gt;
| LocalItemData()&lt;br /&gt;
| Returns a pointer to the user data of a LocalContextItem (LCI).&lt;br /&gt;
|-&lt;br /&gt;
| StoreLocalItem()&lt;br /&gt;
| Insert a LocalContextItem (LCI).&lt;br /&gt;
|-&lt;br /&gt;
| StoreItemInContext()&lt;br /&gt;
| Store a LocalContextItem in a given context node.&lt;br /&gt;
|-&lt;br /&gt;
| FindPropContext()&lt;br /&gt;
| Find the property context for the current state.&lt;br /&gt;
|-&lt;br /&gt;
| FindLocalItem()&lt;br /&gt;
| Return a LocalContextItem from the context stack.&lt;br /&gt;
|-&lt;br /&gt;
| FreeLocalItem()&lt;br /&gt;
| Free a LocalContextItem (LCI) created with AllocLocalItem().&lt;br /&gt;
|-&lt;br /&gt;
| SetLocalItemPurge()&lt;br /&gt;
| Set purge vector for a local context item.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Fredrik Wikstrom</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=IFFParse_Library&amp;diff=9242</id>
		<title>IFFParse Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=IFFParse_Library&amp;diff=9242"/>
		<updated>2017-10-16T13:43:53Z</updated>

		<summary type="html">&lt;p&gt;Fredrik Wikstrom: Added missing interface pointer in OpenClipboard() and CloseClipboard() calls.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== IFFParse Library ==&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;iffparse.library&#039;&#039; was created to help simplify the job of parsing IFF files. Unlike other IFF libraries, iffparse.library is not form-specific. This means that the job of interpreting the structure and contents of the IFF file is up to you. Previous IFF file parsers were either simple but not general, or general but not simple. IFFParse tries to be both simple &#039;&#039;and&#039;&#039; general.&lt;br /&gt;
&lt;br /&gt;
== The Structure of IFF Files ==&lt;br /&gt;
&lt;br /&gt;
Many people have a misconception that IFF means image files. This is not the case. IFF (short for Interchange File Format) is a method of portably storing structured information in machine-readable form. The actual information can be anything, but the manner in which it is stored is very specifically detailed. This specification is the IFF standard.&lt;br /&gt;
&lt;br /&gt;
The IFF standard was originally designed in 1985 by Electronic Arts in conjunction with a committee of developers. The full standard along with file descriptions and sample code is available at [[IFF_Standard|IFF Standard]].&lt;br /&gt;
&lt;br /&gt;
The goal of the IFF standard is to allow customers to move their own data between independently developed software products. The types of data objects to exchange are open-ended and currently include plain and formatted text, raster and structured graphics, fonts, music, sound effects, musical instrument descriptions, and animation. IFF addresses these needs by defining a standard for self-identifying file structures and rules for accessing these files.&lt;br /&gt;
&lt;br /&gt;
=== Chunks: The Building Blocks of IFF ===&lt;br /&gt;
&lt;br /&gt;
IFF files contain various types and amounts of data grouped in data &#039;&#039;chunks&#039;&#039;, each starting with a four-letter ASCII identifier (the chunk ID) followed by a 32-bit length count (the chunk size). The identifier and length count make it possible for IFF readers to skip over chunks that they don’t understand or don’t care about, and extract only the information they need. It may be helpful to think of these chunks as the building blocks of an IFF file.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig33-1.png|frame|center|The Chunk - The Building Block of IFF]]&lt;br /&gt;
&lt;br /&gt;
The ‘CKID’ (chunk ID) characters of a real chunk will be a four letter identifier such as ‘BODY’ or ‘CMAP’, specifying the type and format of data stored in the chunk. Most chunks contain a simple defined grouping of byte, word, and long variables similar to the contents of a C structure. Others such as sound sample and bitmap image body chunks, contain a stream of compressed data.&lt;br /&gt;
&lt;br /&gt;
Chunk writing is fairly straightforward with the one caveat that all chunks must be word-aligned. Therefore an odd-length chunk must be followed by a pad byte of zero (which is not counted in the size of the chunk). When chunks are nested, the enclosing chunk must state the total size of its composite contents (including any pad bytes).&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About Chunk Length|text=Every IFF data chunk begins with a 4-character identifier field followed by a 32-bit size field (these 8 bytes are sometimes referred to as the chunk header). The size field is a count of the rest of the data bytes in the chunk, &#039;&#039;not&#039;&#039; including any pad byte. Hence, the total space occupied by a chunk is given by its size field (rounded to the nearest even number) + 8 bytes for the chunk header}}&lt;br /&gt;
&lt;br /&gt;
=== Composite Data Types ===&lt;br /&gt;
&lt;br /&gt;
Standard IFF files generally contain a variable number of chunks within one of the standard IFF composite data types (which may be thought of as chunks which contain other chunks). The IFF specification defines three basic composite data types, ‘FORM’, ‘CAT ’, and ‘LIST’. A special composite data type, the ‘PROP’, is found only inside a LIST.&lt;br /&gt;
&lt;br /&gt;
Usage of Composite IFF Types&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| FORM || A grouping of chunks which describe one set of data&lt;br /&gt;
|-&lt;br /&gt;
| LIST || A grouping of the same type of FORMs with shared properties in a PROP&lt;br /&gt;
|-&lt;br /&gt;
| PROP || May appear in a LIST to define chunks shared by several FORMs&lt;br /&gt;
|-&lt;br /&gt;
| CAT || A concatenation of related FORMs or LISTs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The special IFF composite data types, like simple chunks, start with a 4-character identifier (such as ‘FORM’), followed by a 32-bit length. But their data begins with a second 4-character identifier that tells you what type of data is in the composite. For example, a FORM ILBM contains chunks describing a bitmap image, and a FORM FTXT contains chunks describing formatted text.&lt;br /&gt;
&lt;br /&gt;
It may help to think of each composite data type as a box containing chunks, the IFF building blocks. The following figure shows a diagram of a typical composite.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig33-2.png|frame|center|The FORM – The Most Common IFF File Type]]&lt;br /&gt;
&lt;br /&gt;
The example FORM in the previous figure is outlined below showing the size of chunks within the FORM. Notice that the size of a composite &#039;&#039;includes&#039;&#039; its second 4-character identifier (shown below as ‘NAME’).&lt;br /&gt;
&lt;br /&gt;
 . FORM 72 NAME  (72 is the size of the FORM starting with the “N” of NAME)&lt;br /&gt;
 . . CKID 22     (then 22 bytes of data, so chunk header + chunk size is 30)&lt;br /&gt;
 . . CKID 10     (then 10 bytes of data, so chunk header + chunk size is 18)&lt;br /&gt;
 . . CKID 12     (then 12 bytes of data, so chunk header + chunk size is 20)&lt;br /&gt;
&lt;br /&gt;
{{Note|text=In the example above, indentation represents the nesting of the chunks within the FORM. Real FORMs and chunks would have different four-character identifiers rather than ‘NAME’ and ‘CKID’.}}&lt;br /&gt;
&lt;br /&gt;
Any odd-length chunks must have a pad byte of zero at the end of the chunk. As shown below, this pad byte is not counted in the size of the chunk but &#039;&#039;is&#039;&#039; counted in the size of any composite types (such as FORM) that contain an odd-length chunk. &#039;&#039;&#039;Warning:&#039;&#039;&#039; some IFF readers and writers do not deal with this properly.&lt;br /&gt;
&lt;br /&gt;
 . FORM 72 NAME  (72 is the size of the FORM starting with the “N” of NAME)&lt;br /&gt;
 . . CKID 21     (then 21 bytes of data, so chunk header + chunk size is 29)&lt;br /&gt;
 . . 0           (pad byte of zero, size 1)&lt;br /&gt;
 . . CKID 10     (then 10 bytes of data, so chunk header + chunk size is 18)&lt;br /&gt;
 . . CKID 12     (then 12 bytes of data, so chunk header + chunk size is 20)&lt;br /&gt;
&lt;br /&gt;
Most IFF files are of the composite type FORM. Generally, a FORM is a simple grouping of chunks that provide information about some data, and the data itself. Although some standard chunks have common usage within different FORMS, the identity and format of most chunks within a FORM are relative to the definition and specification of the FORM. For example, a CRNG chunk in a FORM ILBM may have a totally different format and contents than a chunk named CRNG in a different type of FORM.&lt;br /&gt;
&lt;br /&gt;
One of the most popular IFF FORMs that has been defined is the ILBM standard. This is how most Amiga bitmap imagery is stored. Since this is the most common IFF file, it is used frequently as an example.&lt;br /&gt;
&lt;br /&gt;
Here is the output of a program called “Sift.c” that displays a text description of the contents of an IFF file ([[#IFF Scanner Example|Sift.c]] is listed at the end of this article). The file shown below is a fairly simple ILBM.&lt;br /&gt;
&lt;br /&gt;
 . FORM 11068 ILBM&lt;br /&gt;
 . . BMHD 20&lt;br /&gt;
 . . CAMG 4&lt;br /&gt;
 . . CMAP 12&lt;br /&gt;
 . . BODY 10995&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Computing the Size of a FORM|text=The size of the FORM (11,068 bytes) is equal to the sum of the sizes stated in each chunk contained within the FORM, plus 8 bytes for the overhead of each chunk header (4 bytes for the 4-character chunk ID, and 4 bytes for the 32-bit chunk size), plus 4 bytes for the FORM’s own 4-character ID (‘ILBM’), plus 1 byte more for each pad byte that follow any odd-length chunks.}}&lt;br /&gt;
&lt;br /&gt;
== Parsing an IFF File ==&lt;br /&gt;
&lt;br /&gt;
Chunk reading requires a parser to scan each chunk and dispatch the proper access/conversion procedure. For a simple IFF file, such parsing may be relatively easy, consisting mainly reading in the data of desired chunks, and seeking over unwanted chunks (and the pad byte after odd-length chunks). Interpreting nested chunks is more complex, and requires a method for keeping track of the current context, i.e., the data which is still relevant at any particular depth into the nest. The original IFF specifications compare an IFF file to a C program. Such a metaphor can be useful in understanding the scope of of the chunks in an IFF file.&lt;br /&gt;
&lt;br /&gt;
The IFFParse library addresses general IFF parsing requirements by providing a run-time library which can extract the chunks you want from an IFF file, with the ability to pass control to you when it reaches a chunk that requires special processing such as decompression. IFFParse also understands complex nested IFF formats and will keep track of the context for you.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functions and Structures of IFFParse Library ===&lt;br /&gt;
&lt;br /&gt;
The structures and flags of the IFFParse library are defined in the include files &amp;amp;lt;libraries/iffparse.h&amp;amp;gt; and &amp;amp;lt;libraries/iffparse.i&amp;amp;gt;. IFF files are manipulated through a structure called an IFFHandle. Only some of the fields in the IFFHandle are publicly documented. The rest are managed internally by IFFParse. This handle is passed to all IFFParse functions, and contains the current parse state and position in the file. An IFFHandle is obtained by calling AllocIFF(), and freed through FreeIFF(). This is the only legal way to obtain and dispose of an IFFHandle.&lt;br /&gt;
&lt;br /&gt;
The public portion of if IFFHandle is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Structure associated with an active IFF stream.&lt;br /&gt;
 * &amp;quot;iff_Stream&amp;quot; is a value used by the client&#039;s read/write/seek functions -&lt;br /&gt;
 * it will not be accessed by the library itself and can have any value&lt;br /&gt;
 * (could even be a pointer or a BPTR).&lt;br /&gt;
 */&lt;br /&gt;
struct IFFHandle {&lt;br /&gt;
        ULONG   iff_Stream;&lt;br /&gt;
        ULONG   iff_Flags;&lt;br /&gt;
        LONG    iff_Depth;      /*  Depth of context stack.  */&lt;br /&gt;
        /*  There are private fields hiding here.  */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stream Management ==&lt;br /&gt;
&lt;br /&gt;
A stream is a linear array of bytes that may be accessed sequentially or randomly. DOS files are streams. IFFParse uses Hook structures (defined in &amp;amp;lt;utility/hooks.h&amp;amp;gt;) to implement a general stream management facility. This allows the IFFParse library to read, write, and seek any type of file handle or device by using an application-provided hook function to open, read, write, seek and close the stream.&lt;br /&gt;
&lt;br /&gt;
Built on top of this facility, IFFParse has two internal stream managers: one for unbuffered DOS files (AmigaDOS filehandles), and one for the Clipboard.&lt;br /&gt;
&lt;br /&gt;
=== Initialization ===&lt;br /&gt;
&lt;br /&gt;
As shown above, the IFFHandle structure contains the public field iff_Stream. This is a longword that must be initialized to contain something meaningful to the stream manager. IFFParse never looks at this field itself (at least not directly). Your iff_Stream may be an AmigaDOS filehandle, or an IFFParse ClipboardHandle, or a custom stream of any type if you provide your own custom stream handler (see the section on [[#Custom Stream Handlers|Custom Stream Handlers]]). You must initialize your IFFHandle structure’s iff_Stream to your stream, and then initialize the IFFHandle’s flags and stream hook.&lt;br /&gt;
&lt;br /&gt;
Three sample initializations are shown here. In the case of the internal DOS stream manager, iff_Stream is an AmigaDOS filehandle as returned by Open():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) IDOS-&amp;gt;Open (&amp;quot;filename&amp;quot;, MODE_OLDFILE);&lt;br /&gt;
if(iff-&amp;gt;iff_Stream) IIFFParse-&amp;gt;InitIFFasDOS (iff);  /* use internal DOS stream manager */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the case of the internal Clipboard stream manager, iff_Stream is a pointer to an IFFParse ClipboardHandle structure (the OpenClipboard() call used here is a function of iffparse.library, not the clipboard.device):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard (PRIMARY_CLIP);&lt;br /&gt;
IIFFParse-&amp;gt;InitIFFasClip (iff); /* use internal Clipboard stream manager  */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When using a custom handle such as an fopen() file handle, or a device other than the clipboard, you must provide your own flags and stream handler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) OpenMyCustomStream(&amp;quot;foo&amp;quot;);&lt;br /&gt;
IIFFParse-&amp;gt;InitIFF (iff, IFFF_FSEEK | IFFF_RSEEK, &amp;amp;mystreamhook);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
IFFParse “knows” the seek capabilities of DOS and ClipboardHandle streams, so InitIFFasDOS() and InitIFFasClip() set the flags for you.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=You May Change the Seek Bits in iff_Flags|text=IFFParse sets IFFF_FSEEK | IFFF_RSEEK for DOS files. This is not always true (e.g., pipes). If you know that a DOS file has different seek characteristics, your application may correct the seek bits in iff_Flags after calling InitIFFasDOS().}}&lt;br /&gt;
&lt;br /&gt;
When using InitIFF() to provide a custom handler, you must also provide flags to tell IFFParse the capabilities of your stream. The flags are:&lt;br /&gt;
&lt;br /&gt;
; IFFF_FSEEK&lt;br /&gt;
: This stream has forward-seek capability only.&lt;br /&gt;
&lt;br /&gt;
; IFFF_RSEEK&lt;br /&gt;
: This stream has random-seek capability. IFFF_RSEEK tends to imply IFFF_FSEEK, but it’s best to specify both.&lt;br /&gt;
&lt;br /&gt;
If neither flag is specified, you’re telling IFFParse that it can’t seek through the stream.&lt;br /&gt;
&lt;br /&gt;
After your stream is initialized, call OpenIFF():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;OpenIFF (iff, IFFF_READ);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you establish a read/write mode (by passing IFFF_READ or IFFF_WRITE), you remain in that mode until you call CloseIFF().&lt;br /&gt;
&lt;br /&gt;
=== Termination ===&lt;br /&gt;
&lt;br /&gt;
Termination is simple. Just call CloseIFF(iff). This may be called at any time, and terminates IFFParse’s transaction with the stream. The stream itself is not closed. The IFFHandle may be reused; you may safely call OpenIFF() on it again. You are responsible for closing the streams you opened. A stream used in an IFFHandle must generally remain open until you CloseIFF().&lt;br /&gt;
&lt;br /&gt;
=== Custom Streams ===&lt;br /&gt;
&lt;br /&gt;
A custom stream handler can allow you (and iffparse.library) to use your compiler’s own file I/O functions such as fopen(), fread() and fwrite(), rather than the lower-level AmigaDOS equivalents Open(), Read(), and Write(). A custom stream handler could also be used to read or write IFF files from an Exec device or an unusual handler or file system.&lt;br /&gt;
&lt;br /&gt;
If you install your own stream handler function, iffparse.library will call your function whenever it needs to read, write, or seek on your file. Your stream handler function will then perform these stream actions for iffparse.library. See the “Custom Stream Handlers” section for more information on custom stream handlers.&lt;br /&gt;
&lt;br /&gt;
== Parsing and Writing IFF ==&lt;br /&gt;
&lt;br /&gt;
For information on parsing IFF see [[Parsing_IFF|Parsing IFF]].&lt;br /&gt;
&lt;br /&gt;
For information on writing IFF see [[Writing_IFF|Writing IFF]].&lt;br /&gt;
&lt;br /&gt;
== Context Functions ==&lt;br /&gt;
&lt;br /&gt;
Internally, IFFParse maintains IFF nesting and scoping context via a &#039;&#039;context stack&#039;&#039;. The PushChunk() and PopChunk() functions get their names from this basic idea of the iffparse.library. Direct access to this stack is not allowed. However, many functions are provided to assist in examining and manipulating the context stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About the Context Stack|text=It is probably easier to think of a stack of blocks on a table in front of you when reading this discussion.}}&lt;br /&gt;
&lt;br /&gt;
As the nesting level increases (as would happen when parsing a nested LIST or FORM), the depth of the context stack increases; new elements are added to the top. When these contexts expire, the ContextNodes are deleted and the stack shrinks.&lt;br /&gt;
&lt;br /&gt;
=== Context Nodes ===&lt;br /&gt;
&lt;br /&gt;
The current context is said to be the top element on the stack. Contextual information is stored in a structure called a ContextNode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ContextNode {&lt;br /&gt;
        struct MinNode  cn_Node;&lt;br /&gt;
        LONG            cn_ID;&lt;br /&gt;
        LONG            cn_Type;&lt;br /&gt;
        LONG            cn_Size;        /*  Size of this chunk             */&lt;br /&gt;
        LONG            cn_Scan;        /*  # of bytes read/written so far */&lt;br /&gt;
        /*  There are private fields hiding here.  */&lt;br /&gt;
        };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CurrentChunk() ====&lt;br /&gt;
&lt;br /&gt;
You can obtain a pointer to the current ContextNode through the function CurrentChunk():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
currentnode = IIFFParse-&amp;gt;CurrentChunk (iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ContextNode tells you the type, ID, and size of the currently active chunk. If there is no currently active context, NULL is returned.&lt;br /&gt;
&lt;br /&gt;
==== ParentChunk() ====&lt;br /&gt;
&lt;br /&gt;
To find the parent of a context, you call ParentChunk() on the relevant ContextNode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
parentnode = IIFFParse-&amp;gt;ParentChunk(currentnode);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If there is no parent context, NULL is returned.&lt;br /&gt;
&lt;br /&gt;
=== The Default Context ===&lt;br /&gt;
&lt;br /&gt;
When you first obtain an IFFHandle through AllocIFF(), a hidden default context node is created. You cannot get direct access to this node through CurrentChunk() or ParentChunk(). However, using StoreLocalItem(), you can store information in this context.&lt;br /&gt;
&lt;br /&gt;
=== Context-Specific Data: LocalContextItems ===&lt;br /&gt;
&lt;br /&gt;
ContextNodes can contain application data specific to that context. These data objects are called LocalContextItems. LocalContextItems (LCIs) are a grey-box structure which contain a type, ID and identification field. LCIs are used to store context-sensitive data. The format of this data is application-defined. A ContextNode can contain as many LCIs as you want.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig33-3.png|frame|center|IFFParse Context Stack]]&lt;br /&gt;
&lt;br /&gt;
The previous figure shows the relationship between the IFFHandle, the ContextNodes, and the LCIs. The IFFHandle is the root of the tree, so to speak. ContextNodes “grow” out of the IFFHandle. In turn, LCIs &amp;quot;grow&amp;quot; out of the ContextNodes. What grows out of an LCI is client-defined.&lt;br /&gt;
&lt;br /&gt;
==== AllocLocalItem() ====&lt;br /&gt;
&lt;br /&gt;
To create an LCI, you use the function AllocLocalItem():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
lci = IIFFParse-&amp;gt;AllocLocalItem (type, id, ident, datasize);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If successful, you will be returned a pointer to an LCI having the specified type, ID, and identification values; and with datasize bytes of buffer space for your application to use.&lt;br /&gt;
&lt;br /&gt;
==== LocalItemData() ====&lt;br /&gt;
&lt;br /&gt;
To get a pointer to an LCIs data buffer, you use LocalItemData():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
buf = IIFFParse-&amp;gt;LocalItemData (lci);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may read and write the buffer to your heart’s content; it is yours. You should not, however, write beyond the end of the buffer. The size of the buffer is what you asked for when you called AllocLocalItem().&lt;br /&gt;
&lt;br /&gt;
=== Storing LCIs ===&lt;br /&gt;
&lt;br /&gt;
Once you’ve created and initialized an LCI, you’ll want to attach it to a ContextNode. Though a ContextNode can have many LCIs, a given LCI can be linked to only one ContextNode. Once linked, an LCI cannot be removed from a ContextNode (this may change in the future). Storing an LCI in a ContextNode is done with the functions StoreLocalItem() and StoreItemInContext().&lt;br /&gt;
&lt;br /&gt;
==== StoreLocalItem() ====&lt;br /&gt;
&lt;br /&gt;
The StoreLocalItem() function is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = StoreLocalItem (iff, lci, position);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The position argument determines where the LCI is stored. The possible values are IFFSLI_ROOT, IFFSLI_TOP, and IFFSLI_PROP.&lt;br /&gt;
&lt;br /&gt;
; IFFSLI_ROOT&lt;br /&gt;
: causes StoreLocalItem() to store your LCI in the default ContextNode.&lt;br /&gt;
&lt;br /&gt;
; IFFSLI_TOP&lt;br /&gt;
: gets your LCI stored in the top (current) ContextNode.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=The LCI Ends When the Current Context Ends|text=When the current context expires, your LCI will be deleted by the parser.}}&lt;br /&gt;
&lt;br /&gt;
IFFSLI_PROP causes your LCI to be stored in the topmost context from which a property would apply. This is usually the topmost FORM or LIST chunk. For example, suppose you had a deeply nested ILBM FORM, and you wanted to store the BMHD property in its correct context such that, when the current FORM context expired, the BMHD property would be deleted. IFFSLI_PROP will cause StoreLocalItem() to locate the proper context for such scoping, and store the LCI there. See the section on &amp;quot;Finding the Prop Context&amp;quot; for additional information on the scope of properties.&lt;br /&gt;
&lt;br /&gt;
==== StoreItemInContext() ====&lt;br /&gt;
&lt;br /&gt;
StoreItemInContext() is used when you already have a pointer to the ContextNode to which you want to attach your LCI. It is called like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IIFFParse-&amp;gt;StoreItemInContext (iff, lci, contextnode);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
StoreItemInContext() links your LCI into the specified ContextNode. Then it searches the ContextNode to see if there is another LCI with the same type, ID, and identification values. If so, the old one is deleted.&lt;br /&gt;
&lt;br /&gt;
==== FindLocalItem() ====&lt;br /&gt;
&lt;br /&gt;
After you’ve stored your LCI in a ContextNode, you will no doubt want to be able to find it again later. You do this with the function FindLocalItem(), which is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
lci = IIFFParse-&amp;gt;FindLocalItem (iff, type, id, ident);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FindLocalItem() attempts to locate an LCI having the specified type, ID, and identification values. The search proceeds as follows (refer to the previous figure to understand this better).&lt;br /&gt;
&lt;br /&gt;
FindLocalItem() starts at the top (current) ContextNode and searches all LCIs in that context. If no matching LCIs are found, it proceeds down the context stack to the next ContextNode and searches all its LCIs. The process repeats until it finds the desired LCI (whereupon it returns a pointer to it), or reaches the end without finding anything (where it returns NULL).&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Context Stack Position|text=LCIs higher in the stack will &amp;quot;override&amp;quot; lower LCIs with the same type, ID, and identification field. This is how property scoping is handled. As ContextNodes are popped off the context stack, all its LCIs are deleted as well. See the section on “Freeing LCIs” below for additional information on deleting LCIs.}}&lt;br /&gt;
&lt;br /&gt;
=== Some Interesting Internal Details ===&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=This section details some internal implementation details of iffparse.library which may help you to understand it better. Use of the following information to do “clever” things in an application is forbidden and unsupportable. Don’t even think about it.}}&lt;br /&gt;
&lt;br /&gt;
It turns out that StoredProperties, CollectionItems, and entry and exit handlers are all implemented using LCIs. For example, when you call FindProp(), you are actually calling a front-end to FindLocalItem(). The mysterious identification value (which has heretofore never been discussed) is a value which permits you to differentiate between LCIs having the same type and ID.&lt;br /&gt;
&lt;br /&gt;
For instance, suppose you called PropChunk(), asking it to store an ILBM BMHD. PropChunk() will install an entry handler in the form of an LCI, having type equal to ‘ILBM’, ID equal to ‘BMHD’, and an identification value of IFFLCI_ENTRYHANDLER.&lt;br /&gt;
&lt;br /&gt;
When an ILBM BMHD is encountered, the entry handler is called, and it creates and stores another LCI having type equal to ‘ILBM’, ID equal to ‘BMHD’ and an identification value of IFFLCI_PROP.&lt;br /&gt;
&lt;br /&gt;
Thus, when you call FindProp(), it merely calls FindLocalItem() with your type and ID, and supplies IFFLCI_PROP for the identification value.&lt;br /&gt;
&lt;br /&gt;
Therefore, handlers, StoredProperties, CollectionItems and your own custom LCIs can never be confused with each other, since they all have unique identification values. Since they are all handled (and searched for) in the same way, they all “override” each other in a consistent way. Just as StoredProperties higher in the context stack will be found and returned before identical ones in lower contexts, so will chunk handlers be found and invoked before ones lower on the context stack (recall FindLocalItem()’s search procedure).&lt;br /&gt;
&lt;br /&gt;
This means you can temporarily override a chunk handler by installing an identical handler in a higher context. The handler will persist until the context in which it is stored expires, after which, the original one regains scope.&lt;br /&gt;
&lt;br /&gt;
== Error Handling ==&lt;br /&gt;
&lt;br /&gt;
If at any time during reading or writing you encounter an error, the IFFHandle is left in an undefined state. Upon detection of an error, you should perform an abort sequence and CloseIFF() the IFFHandle. Once CloseIFF() has been called, the IFFHandle is restored to normalcy and may be reused.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
&lt;br /&gt;
This section discusses customizing of IFFParse data handling. For applications with special needs, IFFParse supports both custom stream handlers and custom chunk handlers.&lt;br /&gt;
&lt;br /&gt;
=== Custom Stream Handlers ===&lt;br /&gt;
&lt;br /&gt;
As discussed earlier, IFFParse contains built-in stream handlers for AmigaDOS file handles as returned by Open(), and for the clipboard.device. If you are using AmigaDOS filehandles or the clipboard.device, you need not supply a custom stream handler.&lt;br /&gt;
&lt;br /&gt;
If you wish to use your compiler’s own file I/O functions (such as fread() ) or need to read or write to an unusual handler or Exec device, you must provide a custom stream handler for your IFFHandle. Your custom stream handler will be called to perform all reads, writes, and seeks on your custom stream. The allows you to use compiler file I/O functions, Exec device commands, or any other method to perform the requested stream operations.&lt;br /&gt;
&lt;br /&gt;
If you are implementing your own custom stream handler, you will need to know the mechanics of hook call-backs, and how to interpret the parameters. An IFFParse custom stream handler is simply a function in your code that follows hook function conventions (Hook functions are also known as callback functions. See [[Utility_Library|Utility Library]] for more details).&lt;br /&gt;
&lt;br /&gt;
==== Installing a Custom Stream Handler ====&lt;br /&gt;
&lt;br /&gt;
To initialize your IFFHandle to point to your custom stream and stream handler, you must open your stream and place a pointer to the stream in your IFFHandle’s iff_Stream field. This pointer could be the return from fopen(), or an Exec device I/O request, or any other type of pointer which will provide your custom stream handler with a way to manage your stream. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) fopen(&amp;quot;foo&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, you must install your custom handler for this stream, and also tell IFFParse the seek capabilities of your stream. To install your custom stream handler, you must first set up a Hook structure (&amp;amp;lt;utility/hooks.h&amp;amp;gt;) to point to your stream handling function. Then use InitIFF() to install your stream handler and also tell IFFParse the seek capabilities of your stream. There is “some assembly required”.&lt;br /&gt;
&lt;br /&gt;
For an IFFParse custom stream hook the function prototype looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 mystreamhandler(struct Hook *hook, struct IFFHandle *iff, struct IFFStreamCmd *actionpkt);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A return of 0 indicates success. A non-zero return indicates an error.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
static int32 mystreamhandler&lt;br /&gt;
(&lt;br /&gt;
    struct Hook         *hook,&lt;br /&gt;
    struct IFFHandle    *iff,&lt;br /&gt;
    struct IFFStreamCmd *actionpkt&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
/*&lt;br /&gt;
 * Code to handle the stream commands - see end this section&lt;br /&gt;
 * for a complete example custom stream handler function.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Initialization of Hook structure for registerized handler function */&lt;br /&gt;
struct Hook mystreamhook&lt;br /&gt;
{&lt;br /&gt;
  { NULL },&lt;br /&gt;
  (HOOKFUNC) mystreamhandler,  /* h_Entry, registerized function entry */&lt;br /&gt;
  NULL,&lt;br /&gt;
  NULL&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Open custom stream and InitIFF to custom stream handler */&lt;br /&gt;
if ( iff-&amp;gt;iff_Stream = (ULONG) myopen(&amp;quot;foo&amp;quot;) )&lt;br /&gt;
        {&lt;br /&gt;
        IIFFParse-&amp;gt;InitIFF (iff, IFFF_FSEEK | IFFF_RSEEK, &amp;amp;mystreamhook);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Inside a Custom Stream Handler ====&lt;br /&gt;
&lt;br /&gt;
When the library calls your stream handler, you’ll be passed a pointer to the Hook structure (hook in the example used here), a pointer to the IFFHandle (iff), and a pointer to an IFFStreamCmd structure (actionpkt). Your stream pointer may be found in iff-&amp;amp;gt;iff_Stream where you previously stored it. The IFFStreamCmd (actionpkt) will describe the action that IFFParse needs you to perform on your stream:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Custom stream handler is passed struct IFFStreamCmd *actionpkt */&lt;br /&gt;
struct IFFStreamCmd {&lt;br /&gt;
        LONG    sc_Command;     /*  Operation to be performed (IFFCMD_) */&lt;br /&gt;
        APTR    sc_Buf;         /*  Pointer to data buffer              */&lt;br /&gt;
        LONG    sc_NBytes;      /*  Number of bytes to be affected      */&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
/* Possible call-back command values.  (Using 0 as the value for IFFCMD_INIT&lt;br /&gt;
 * was, in retrospect, probably a bad idea.)&lt;br /&gt;
 */&lt;br /&gt;
#define IFFCMD_INIT     0       /*  Prepare the stream for a session    */&lt;br /&gt;
#define IFFCMD_CLEANUP  1       /*  Terminate stream session            */&lt;br /&gt;
#define IFFCMD_READ     2       /*  Read bytes from stream              */&lt;br /&gt;
#define IFFCMD_WRITE    3       /*  Write bytes to stream               */&lt;br /&gt;
#define IFFCMD_SEEK     4       /*  Seek on stream                      */&lt;br /&gt;
#define IFFCMD_ENTRY    5       /*  You just entered a new context      */&lt;br /&gt;
#define IFFCMD_EXIT     6       /*  You&#039;re about to leave a context     */&lt;br /&gt;
#define IFFCMD_PURGELCI 7       /*  Purge a LocalContextItem            */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Your custom stream handler should perform the requested action on your custom stream, and then return 0 for success or an IFFParse error if an error occurred. The following code demonstrates a sample stream handler for a stream which was opened with a compiler’s fopen() buffered file I/O function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
static int32 mystreamhandler ( struct Hook *hook,&lt;br /&gt;
                              struct IFFHandle *iff,&lt;br /&gt;
                              struct IFFStreamCmd *actionpkt )&lt;br /&gt;
{&lt;br /&gt;
        FILE   *stream;&lt;br /&gt;
        int32  nbytes, error;&lt;br /&gt;
        uint8  *buf;&lt;br /&gt;
&lt;br /&gt;
        stream  = (FILE *) iff-&amp;gt;iff_Stream;     /* get your stream pointer */&lt;br /&gt;
        nbytes  = actionpkt-&amp;gt;sc_NBytes;         /* length for command */&lt;br /&gt;
        buf     = (uint8 *) actionpkt-&amp;gt;sc_Buf;  /* buffer for the command */&lt;br /&gt;
&lt;br /&gt;
        /* Now perform the action specified by the actionpkt-&amp;amp;gt;sc_Command */&lt;br /&gt;
&lt;br /&gt;
        switch (actionpkt-&amp;gt;sc_Command) {&lt;br /&gt;
        case IFFCMD_READ:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_READ means read sc_NBytes from the stream and place&lt;br /&gt;
                 * it in the memory pointed to by sc_Buf.  Be aware that&lt;br /&gt;
                 * sc_NBytes may be larger than can be contained in an int.&lt;br /&gt;
                 * This is important if you plan on recompiling this for&lt;br /&gt;
                 * 16-bit ints, since fread() takes int arguments.&lt;br /&gt;
                 *&lt;br /&gt;
                 * Any error code returned will be remapped by IFFParse into&lt;br /&gt;
                 * IFFERR_READ.&lt;br /&gt;
                 */&lt;br /&gt;
                error = (fread (buf, 1, nbytes, stream) != nbytes);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
        case IFFCMD_WRITE:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_WRITE is analogous to IFFCMD_READ.&lt;br /&gt;
                 *&lt;br /&gt;
                 * Any error code returned will be remapped by IFFParse into&lt;br /&gt;
                 * IFFERR_WRITE.&lt;br /&gt;
                 */&lt;br /&gt;
                error = (fwrite (buf, 1, nbytes, stream) != nbytes);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
        case IFFCMD_SEEK:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_SEEK asks that you performs a seek relative to the&lt;br /&gt;
                 * current position.  sc_NBytes is a signed number,&lt;br /&gt;
                 * indicating seek direction (positive for forward, negative&lt;br /&gt;
                 * for backward).  sc_Buf has no meaning here.&lt;br /&gt;
                 *&lt;br /&gt;
                 * Any error code returned will be remapped by IFFParse into&lt;br /&gt;
                 * IFFERR_SEEK.&lt;br /&gt;
                 */&lt;br /&gt;
                error = (fseek (stream, nbytes, 1) == -1);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
        case IFFCMD_INIT:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_INIT means to prepare your stream for reading.&lt;br /&gt;
                 * This is used for certain streams that can&#039;t be read&lt;br /&gt;
                 * immediately upon opening, and need further preparation.&lt;br /&gt;
                 * This operation is allowed to fail;  the error code placed&lt;br /&gt;
                 * in D0 will be returned directly to the client.&lt;br /&gt;
                 *&lt;br /&gt;
                 * An example of such a stream is the clipboard.  The&lt;br /&gt;
                 * clipboard.device requires you to set the io_ClipID and&lt;br /&gt;
                 * io_Offset to zero before starting a read.  You would&lt;br /&gt;
                 * perform such a sequence here.  (Stdio files need no such&lt;br /&gt;
                 * preparation, so we simply return zero for success.)&lt;br /&gt;
                 */&lt;br /&gt;
        case IFFCMD_CLEANUP:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_CLEANUP means to terminate the transaction with&lt;br /&gt;
                 * the associated stream.  This is used for streams that&lt;br /&gt;
                 * can&#039;t simply be closed.  This operation is not allowed to&lt;br /&gt;
                 * fail;  any error returned will be ignored.&lt;br /&gt;
                 *&lt;br /&gt;
                 * An example of such a stream is (surprise!) the clipboard.&lt;br /&gt;
                 * It requires you to explicitly end reads by CMD_READing&lt;br /&gt;
                 * past the end of a clip, and end writes by sending a&lt;br /&gt;
                 * CMD_UPDATE.  You would perform such a sequence here.&lt;br /&gt;
                 * (Again, stdio needs no such sequence.)&lt;br /&gt;
                 */&lt;br /&gt;
                error = 0;&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        return (error);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Custom Chunk Handlers ===&lt;br /&gt;
&lt;br /&gt;
Like custom stream handlers, custom chunk handlers are implemented using Hook structures. See the previous section for details on how a handler function may be interfaced using a Hook structure.&lt;br /&gt;
&lt;br /&gt;
There are two types of chunk handlers: entry handlers and exit handlers. Entry handlers are invoked just after the parser enters the chunk; the stream will be positioned to read the first byte in the chunk. (If the chunk is a FORM, LIST, CAT, or PROP, the longword type will be read by the parser; the stream will be positioned to read the byte immediately following the type.) Exit handlers are invoked just before the parser leaves the chunk; the stream is not positioned predictably within the chunk.&lt;br /&gt;
&lt;br /&gt;
==== Installing a Custom Chunk Handler ====&lt;br /&gt;
&lt;br /&gt;
To install an entry handler, you call EntryHandler() in the following manner:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;EntryHandler (iff, type, id, position, hookptr, object);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An exit handler is installed by saying:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;ExitHandler (iff, type, id, position, hookptr, object);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In both cases, a handler is installed for chunks having the specified type and id. The position argument specifies in what context to install the handler, and is identical to the position argument used by StoreLocalItem(). The hookptr argument given above is a pointer to your Hook structure.&lt;br /&gt;
&lt;br /&gt;
==== Inside a Custom Chunk Handler ====&lt;br /&gt;
&lt;br /&gt;
The mechanics of receiving parameters through the Hook are covered in the “Custom Stream Handlers” section, and are not duplicated here. Refer to the EntryHandler() and ExitHandler() Autodocs for the contents of the registers upon entry.&lt;br /&gt;
&lt;br /&gt;
Once inside your handler, you can call nearly all functions in the iffparse.library, however, you should avoid calling ParseIFF() from within chunk handlers.&lt;br /&gt;
&lt;br /&gt;
Your handler runs in the same environment as whoever called ParseIFF(). The propagation sequence is:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;mainline&#039;&#039;&#039; --&#039;&#039;calls&#039;&#039;--&amp;gt; &#039;&#039;&#039;ParseIFF()&#039;&#039;&#039; --&#039;&#039;calls&#039;&#039;--&amp;gt; &#039;&#039;&#039;your_handler()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Thus, your handler runs on your mainline’s stack, and can call any OS functions the mainline code can. (Your handler will have to set the global base pointer if your code uses base-relative addressing.)&lt;br /&gt;
&lt;br /&gt;
The return code will affect the parser in a number of ways:&lt;br /&gt;
&lt;br /&gt;
* If you return zero (a normal, uneventful return), ParseIFF() will continue normally. If you return the value IFFERR_RETURN2CLIENT, ParseIFF() will stop and return the value zero to the mainline code.&lt;br /&gt;
&lt;br /&gt;
* If you return any other value, ParseIFF() will stop and return that value to the mainline code. This is how you should return error conditions to the client code.&lt;br /&gt;
&lt;br /&gt;
==== The Object Parameter ====&lt;br /&gt;
&lt;br /&gt;
The object parameter supplied to EntryHandler() and ExitHandler() is a pointer which will be passed to your handler when invoked. This pointer can be anything; the parser doesn’t use it directly. As an example, you might pass the pointer to the IFFHandle. This way, when your handler is called, you’ll be able to easily perform operations on the IFFHandle within your handler. Such code might appear as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = EntryHandler (iff, ID_ILBM, ID_BMHD, IFFSLI_ROOT, hook, iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And your handler would be declared as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 MyHandler (struct Hook      *hook,&lt;br /&gt;
                 int32            *cmd,&lt;br /&gt;
                 struct IFFHandle *iff)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From within your handler, you could then call CurrentChunk(), ReadChunkBytes(), or nearly any other operation on the IFFHandle. Please refer to the EntryHandler() and ExitHandler() Autodocs for additional information on the use of chunk handlers.&lt;br /&gt;
&lt;br /&gt;
=== Finding the Prop Context ===&lt;br /&gt;
&lt;br /&gt;
Earlier it was mentioned that supplying a position value of IFFSLI_PROP to StoreLocalItem() would store it in the topmost property scope. FindPropContext() is the routine that finds that topmost context.&lt;br /&gt;
&lt;br /&gt;
Property chunks (such as the BMHD, CMAP, and others) have dominion over the FORM that contains them; they are said to be “in scope” and their definition persists until the FORM’s context ends. Thus, a property chunk has a scoping level equal to the FORM that contains it; when the FORM ends, the property dies with it.&lt;br /&gt;
&lt;br /&gt;
Consider a more complicated example. Suppose you have a LIST with a PROP in it. PROPs are the global variables of LISTs; thus a property chunk declared in a PROP will persist until the LIST’s context ends.&lt;br /&gt;
&lt;br /&gt;
This is what FindPropContext() is looking for; a context level in which a property chunk may be installed.&lt;br /&gt;
&lt;br /&gt;
FindPropContext() starts at the parent of the current context (second from the top of the context stack) and starts searching downward, looking for the first FORM or LIST context it finds. If it finds one, it returns a pointer to that ContextNode. If it can’t find a suitable context level, it returns NULL.&lt;br /&gt;
&lt;br /&gt;
FindPropContext() is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ContextNode *cn = IFFParse-&amp;gt;FindPropContext (iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Freeing LCIs ===&lt;br /&gt;
&lt;br /&gt;
Ordinarily, the parser will automatically delete LCIs you have allocated and installed. However, you may have a case where simply FreeVec()ing your LCI is not enough; you may need to free some ancillary memory, or decrement a counter, or send a signal, or something. This is where SetLocalItemPurge() comes in. It is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IFFParse-&amp;gt;SetLocalItemPurge (lci, hookptr);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the parser is ready to delete your LCI, your purge handler code will be called through the Hook you supplied. You can then perform all your necessary operations. One of these operations should be to free the LCI itself. This is done with FreeLocalItem():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IIFFParse-&amp;gt;FreeLocalItem (lci);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This deallocates the memory used to store the LCI and the client buffer allocated with it. FreeLocalItem() is only called as part of a custom purge handler.&lt;br /&gt;
&lt;br /&gt;
As with custom chunk handlers, your purge handler executes in the same environment as the mainline code that called ParseIFF(). It is recommended that you keep purge handlers short and to the point; super clever stuff should be reserved for custom chunk handlers, or for the client’s mainline code. Custom purge handlers must &#039;&#039;always&#039;&#039; work; failures will be ignored.&lt;br /&gt;
&lt;br /&gt;
== IFF FORM Specifications ==&lt;br /&gt;
&lt;br /&gt;
The specifications for Amiga IFF formats are maintained by the AmigaOS Development Team. The latest specifications are published in the [[IFF_Standard|IFF Standard]]. Updates of the IFF Manual, selected new FORMs and changes to existing FORMs are documented on this wiki.&lt;br /&gt;
&lt;br /&gt;
Some of the most commonly used IFF FORMs are the four that were originally specified in the EA IFF-85 standard:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| ILBM || Bitmap images and palettes&lt;br /&gt;
|-&lt;br /&gt;
| FTXT || Simple formatted text&lt;br /&gt;
|-&lt;br /&gt;
| SMUS || Simple musical scores&lt;br /&gt;
|-&lt;br /&gt;
| 8SVX || 8-bit sound samples&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Of these four, ILBM is the most commonly encountered FORM, and FTXT is becoming increasingly important since the &#039;&#039;conclip&#039;&#039; command passes clipped console text through the clipboard as FTXT. All data clipped to the clipboard must be in an IFF format.&lt;br /&gt;
&lt;br /&gt;
This section will provide a brief summary of the ILBM and FTXT FORMs and their most used common chunks. Please consult the EA-IFF specifications for additional information.&lt;br /&gt;
&lt;br /&gt;
=== FORM ILBM ===&lt;br /&gt;
&lt;br /&gt;
The IFF file format for graphic images on the Amiga is called FORM ILBM (InterLeaved BitMap). It follows a standard parsable IFF format.&lt;br /&gt;
&lt;br /&gt;
==== ILBM.BMHD BitMapHeader Chunk ====&lt;br /&gt;
&lt;br /&gt;
The most important chunk in a FORM ILBM is the BMHD (BitMapHeader) chunk which describes the size and compression of the image:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
typedef UBYTE Masking;  /* Choice of masking technique - Usually 0. */&lt;br /&gt;
#define mskNone                 0&lt;br /&gt;
#define mskHasMask              1&lt;br /&gt;
#define mskHasTransparentColor  2&lt;br /&gt;
#define mskLasso                3&lt;br /&gt;
&lt;br /&gt;
/* Compression algorithm applied to the rows of all source&lt;br /&gt;
 * and mask planes. &amp;amp;quot;cmpByteRun1&amp;amp;quot; is byte run encoding.&lt;br /&gt;
 * Do not compress across rows!  Compression is usually 1.&lt;br /&gt;
 */&lt;br /&gt;
typedef UBYTE Compression;&lt;br /&gt;
#define cmpNone                 0&lt;br /&gt;
#define cmpByteRun1             1&lt;br /&gt;
&lt;br /&gt;
/* The BitMapHeader structure expressed as a C structure */&lt;br /&gt;
typedef struct {&lt;br /&gt;
        UWORD w, h;             /* raster width &amp;amp;amp; height in pixels      */&lt;br /&gt;
        WORD  x, y;             /* pixel position for this image        */&lt;br /&gt;
        UBYTE nPlanes;          /* # bitplanes (without mask, if any)   */&lt;br /&gt;
        Masking     masking;    /* One of the values above.  Usually 0  */&lt;br /&gt;
        Compression compression;/* One of the values above.  Usually 1  */&lt;br /&gt;
        UBYTE reserved1;        /* reserved; ignore on read, write as 0 */&lt;br /&gt;
        UWORD transparentColor; /* transparent color number. Usually 0  */&lt;br /&gt;
        UBYTE xAspect, yAspect; /* pixel aspect, a ratio width : height */&lt;br /&gt;
        WORD  pageWidth, pageHeight;    /* source &amp;amp;quot;page&amp;amp;quot; size in pixels */&lt;br /&gt;
} BitMapHeader;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=This hex dump is shown only to help the reader understand how IFF chunks appear in a file. You cannot ever depend on any particular ILBM chunk being at any particular offset into the file. IFF files are composed, in their simplest form, of chunks within a FORM. Each chunk starts with a 4-letter chunkID, followed by a 32-bit length of the rest of the chunk. You must &#039;&#039;parse&#039;&#039; IFF files, skipping past unneeded or unknown chunks by seeking their length (+1 if odd length) to the next 4-letter chunk ID. In a real ILBM file, you are likely to encounter additional optional chunks. See the IFF Specification listed in [[IFF_Standard|IFF Standard]] for additional information on such chunks.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0000: 464F524D 00016418 494C424D 424D4844    FORM..d.ILBMBMHD&lt;br /&gt;
0010: 00000014 01400190 00000000 06000100    .....@..........&lt;br /&gt;
0020: 00000A0B 01400190 43414D47 00000004    .....@..CAMG....&lt;br /&gt;
0030: 00000804 434D4150 00000030 001000E0    ....CMAP...0....&lt;br /&gt;
0040: E0E00000 20000050 30303050 50500030    .... ..P000PPP.0&lt;br /&gt;
0050: 90805040 70707010 60E02060 E06080D0    ..P@ppp.`. `.`..&lt;br /&gt;
0060: A0A0A0A0 90E0C0C0 C0D0A0E0 424F4459    ............BODY&lt;br /&gt;
0070: 000163AC F8000F80 148A5544 2ABDEFFF    ..c.......UD*...  etc.&lt;br /&gt;
&lt;br /&gt;
Interpretation:&lt;br /&gt;
&lt;br /&gt;
      &#039;F O R M&#039; length  &#039;I L B M&#039;&#039;B M H D&#039;&amp;amp;lt;-start of BitMapHeader chunk&lt;br /&gt;
0000: 464F524D 00016418 494C424D 424D4844    FORM..d.ILBMBMHD&lt;br /&gt;
&lt;br /&gt;
       length  WideHigh XorgYorg PlMkCoRe &amp;amp;lt;- Planes Mask Compression Reserved&lt;br /&gt;
0010: 00000014 01400190 00000000 06000100    .....@..........&lt;br /&gt;
&lt;br /&gt;
      TranAspt PagwPagh &#039;C A M G&#039; length  &amp;amp;lt;- start of C-AMiGa View modes chunk&lt;br /&gt;
0020: 00000A0B 01400190 43414D47 00000004    .....@..CAMG....&lt;br /&gt;
&lt;br /&gt;
dir include:&lt;br /&gt;
      ViewMode &#039;C M A P&#039; length   R g b R &amp;amp;lt;- ViewMode 800=HAM | 4=LACE&lt;br /&gt;
0030: 00000804 434D4150 00000030 001000E0    ....CMAP...0....&lt;br /&gt;
&lt;br /&gt;
       g b R g  b R g b  R g b R  g b R g &amp;amp;lt;- Rgb&#039;s are for reg0 thru regN&lt;br /&gt;
0040: E0E00000 20000050 30303050 50500030    .... ..P000PPP.0&lt;br /&gt;
&lt;br /&gt;
       b R g b  R g b R  g b R g  b R g b&lt;br /&gt;
0050: 90805040 70707010 60E02060 E06080D0    ..P@ppp.`. `.`..&lt;br /&gt;
&lt;br /&gt;
       R g b R  g b R g  b R g b &#039;B O D Y&#039;&lt;br /&gt;
0060: A0A0A0A0 90E0C0C0 C0D0A000 424F4459    ............BODY&lt;br /&gt;
&lt;br /&gt;
       length   start of body data        &amp;amp;lt;- Compacted (Compression=1 above)&lt;br /&gt;
0070: 000163AC F8000F80 148A5544 2ABDEFFF    ..c.......UD*...&lt;br /&gt;
0080: FFBFF800 0F7FF7FC FF04F85A 77AD5DFE    ...........Zw.].  etc.&lt;br /&gt;
&lt;br /&gt;
Simple CAMG ViewModes:  HIRES=0x8000  LACE=0x4  HAM=0x800  HALFBRITE=0x80&lt;br /&gt;
&lt;br /&gt;
( ILBMs may contain a LONGWORD ViewPort ModeID in CAMG )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Interpreting ILBMs ====&lt;br /&gt;
&lt;br /&gt;
ILBM is a fairly simple IFF FORM. All you really need to deal with to extract the image are the following chunks:&lt;br /&gt;
&lt;br /&gt;
Information about the size, depth, compaction method (see interpreted hex dump above).&lt;br /&gt;
&lt;br /&gt;
Optional Amiga ViewModes chunk. Most HAM and HALFBRITE ILBMs should have this chunk. If no CAMG chunk is present, and image is 6 planes deep, assume HAM and you’ll probably be right. Some Amiga ViewModes flags are HIRES=0x8000, LACE=0x4, HAM=0x800, HALFBRITE=0x80. 2.0 ILBM writers should write a full 32-bit mode ID in the CAMG. See the IFF Manual for more information on writing and interpreting 32-bit CAMG values.&lt;br /&gt;
&lt;br /&gt;
RGB values for color registers 0 to N. Previously, 4-bit RGB components each left justified in a byte. These should now be stored as a full 8-bit RGB values by duplicating 4-bit values in the high and low nibble of the byte.&lt;br /&gt;
&lt;br /&gt;
The pixel data, stored in an interleaved fashion as follows (each line individually compacted if BMHD Compression = 1):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
plane 0 scan line 0&lt;br /&gt;
plane 1 scan line 0&lt;br /&gt;
plane 2 scan line 0&lt;br /&gt;
...&lt;br /&gt;
plane n scan line 0&lt;br /&gt;
plane 0 scan line 1&lt;br /&gt;
plane 1 scan line 1&lt;br /&gt;
etc.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also watch for AUTH Author chunks and (c) copyright chunks and preserve any copyright information if you rewrite the ILBM.&lt;br /&gt;
&lt;br /&gt;
==== ILBM BODY Compression ====&lt;br /&gt;
&lt;br /&gt;
The BODY contains pixel data for the image. Width, Height, and depth (Planes) is specified in the BMHD.&lt;br /&gt;
&lt;br /&gt;
If the BMHD Compression byte is 0, then the scan line data is not compressed. If Compression=1, then each scan line is individually compressed as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;while (not produced the desired number of bytes)&lt;br /&gt;
&lt;br /&gt;
    /* get a byte, call it N */&lt;br /&gt;
&lt;br /&gt;
    if (N &amp;amp;gt;= 0 &amp;amp;amp;&amp;amp;amp; N &amp;amp;lt;= 127)&lt;br /&gt;
        /* copy the next N+1 bytes literally */&lt;br /&gt;
&lt;br /&gt;
    if (N &amp;amp;gt;= -127 &amp;amp;amp;&amp;amp;amp; N &amp;amp;lt;= -1)&lt;br /&gt;
        /* repeat the next byte N+1 times */&lt;br /&gt;
&lt;br /&gt;
    if (N == -128)&lt;br /&gt;
        /* skip it, presumably it&#039;s padding */&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== Interpreting the Scan Line Data ====&lt;br /&gt;
&lt;br /&gt;
If the ILBM is not HAM or HALFBRITE, then after parsing and uncompacting if necessary, you will have N planes of pixel data. Color register used for each pixel is specified by looking at each pixel thru the planes. For instance, if you have 5 planes, and the bit for a particular pixel is set in planes 0 and 3:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PLANE     4 3 2 1 0&lt;br /&gt;
PIXEL     0 1 0 0 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
then that pixel uses color register binary 01001 = 9.&lt;br /&gt;
&lt;br /&gt;
The RGB value for each color register is stored in the CMAP chunk of the ILBM, starting with register 0, with each register’s RGB value stored as one byte of R, one byte G, and one byte of B, with each component left justified in the byte. (i.e. Amiga R, G, and B components are each stored in the high nibble of a byte)&lt;br /&gt;
&lt;br /&gt;
But, if the picture is HAM or HALFBRITE, it is interpreted differently. Hopefully, if the picture is HAM or HALFBRITE, the package that saved it properly saved a CAMG chunk (look at a hex dump of your file with ASCII interpretation - you will see the chunks - they all start with a 4-ASCII-char chunk ID). If the picture is 6 planes deep and has no CAMG chunk, it is probably HAM. If you see a CAMG chunk, the ‘CAMG’ is followed by the 32-bit chunk length, and then the 32-bit Amiga view mode flags.&lt;br /&gt;
&lt;br /&gt;
HAM pictures will have the 0x800 bit set in CAMG chunk ViewModes. HALFBRITE pictures will have the 0x80 bit set. See the graphics library articles for more information on HAM and HALFBRITE modes.&lt;br /&gt;
&lt;br /&gt;
==== Other ILBM Notes ====&lt;br /&gt;
&lt;br /&gt;
Amiga ILBMs images must be stored as an even number of bytes in width. However, the ILBM BMHD field w (width) should describe the actual image width, not the rounded up width as stored.&lt;br /&gt;
&lt;br /&gt;
ILBMs created with Electronic Arts IBM or Amiga &#039;&#039;Deluxe Paint II&#039;&#039; packages are compatible (though you may have to use a ‘.lbm’ filename extension on an IBM). The ILBM graphic files may be transferred between the machines (or between the Amiga and IBM sides your Amiga if you have a CBM Bridgeboard card installed) and loaded into either package.&lt;br /&gt;
&lt;br /&gt;
=== FORM FTXT ===&lt;br /&gt;
&lt;br /&gt;
The FTXT (Formatted TeXT) form is the standard format for sharing text on the Amiga. The console device clip and paste functions, in conjunction with the startup-sequence &#039;&#039;conclip&#039;&#039; command, pass clipped console text through the clipboard as FTXT.&lt;br /&gt;
&lt;br /&gt;
By supporting reading and writing of clipboard device FTXT, your application will allow users to cut and paste text between your application, other applications, and Amiga Shell windows.&lt;br /&gt;
&lt;br /&gt;
The FTXT form is very simple. Generally, for clip and paste operations, you will only be concerned with the CHRS (character string) chunks of an FTXT. There may be one or more CHRS chunks, each of which contains a non-NULL-terminated string of ASCII characters whose length is equal to the length (StoredProperty.sp_Size) of the chunk.&lt;br /&gt;
&lt;br /&gt;
Be aware that if you CollectionChunk() the CHRS chunks of an FTXT, the list accumulated by IFFParse will be in reverse order from the chunk order in the file. See the ClipFTXT.c example at the end of this article for a simple example of reading (and optionally writing) FTXT to and from the clipboard. See also the [[Clipboard_Device|Clipboard Device]] and [[Console_Device|Console Device]] for more information on console clip and paste.&lt;br /&gt;
&lt;br /&gt;
== IFFParse Examples ==&lt;br /&gt;
&lt;br /&gt;
Two examples follow: “ClipFTXT.c” and “Sift.c”. These are simple examples that demonstrate the use of the IFFParse library. More complex examples for displaying and saving ILBMs may be found in the [[IFF_Standard|IFF Standard]] section.&lt;br /&gt;
&lt;br /&gt;
=== Clipboard FTXT Example ===&lt;br /&gt;
&lt;br /&gt;
ClipFTXT.c demonstrates reading (and optional writing) of clipboard device FTXT. This example may be used as the basis for supporting console pastes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 *&lt;br /&gt;
 * clipftxt.c:   Writes ASCII text to clipboard unit as FTXT&lt;br /&gt;
 *               (All clipboard data must be IFF)&lt;br /&gt;
 *&lt;br /&gt;
 * Usage: clipftxt unitnumber&lt;br /&gt;
 *&lt;br /&gt;
 * To convert to an example of reading only, comment out #define WRITEREAD&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Causes example to write FTXT first, then read it back&lt;br /&gt;
 * Comment out to create a reader only&lt;br /&gt;
 */&lt;br /&gt;
#define WRITEREAD&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define MINARGS 2&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR usage = &amp;quot;Usage: clipftxt unitnumber (use zero for primary unit)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Text error messages for possible IFFERR_#? returns from various&lt;br /&gt;
 * IFF routines.  To get the index into this array, take your IFFERR code,&lt;br /&gt;
 * negate it, and subtract one.&lt;br /&gt;
 *  idx = -error - 1;&lt;br /&gt;
 */&lt;br /&gt;
char    *errormsgs[] = {&lt;br /&gt;
        &amp;quot;End of file (not an error).&amp;quot;,&lt;br /&gt;
        &amp;quot;End of context (not an error).&amp;quot;,&lt;br /&gt;
        &amp;quot;No lexical scope.&amp;quot;,&lt;br /&gt;
        &amp;quot;Insufficient memory.&amp;quot;,&lt;br /&gt;
        &amp;quot;Stream read error.&amp;quot;,&lt;br /&gt;
        &amp;quot;Stream write error.&amp;quot;,&lt;br /&gt;
        &amp;quot;Stream seek error.&amp;quot;,&lt;br /&gt;
        &amp;quot;File is corrupt.&amp;quot;,&lt;br /&gt;
        &amp;quot;IFF syntax error.&amp;quot;,&lt;br /&gt;
        &amp;quot;Not an IFF file.&amp;quot;,&lt;br /&gt;
        &amp;quot;Required call-back hook missing.&amp;quot;,&lt;br /&gt;
        &amp;quot;Return to client.  You should never see this.&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define RBUFSZ 512&lt;br /&gt;
&lt;br /&gt;
#define  ID_FTXT        MAKE_ID(&#039;F&#039;,&#039;T&#039;,&#039;X&#039;,&#039;T&#039;)&lt;br /&gt;
#define  ID_CHRS        MAKE_ID(&#039;C&#039;,&#039;H&#039;,&#039;R&#039;,&#039;S&#039;)&lt;br /&gt;
&lt;br /&gt;
struct IFFParseIFace *IIFFParse;&lt;br /&gt;
&lt;br /&gt;
UBYTE mytext[]=&amp;quot;This FTXT written to clipboard by clipftxt example.\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    struct IFFHandle    *iff = NULL;&lt;br /&gt;
    struct ContextNode  *cn;&lt;br /&gt;
    long                error=0, unitnumber=0, rlen;&lt;br /&gt;
    int textlen;&lt;br /&gt;
    UBYTE readbuf[RBUFSZ];&lt;br /&gt;
&lt;br /&gt;
        /* if not enough args or &#039;?&#039;, print usage */&lt;br /&gt;
        if(((argc)&amp;amp;&amp;amp;(argc&amp;lt;MINARGS))||(argv[argc-1][0]==&#039;?&#039;))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;%s\n&amp;quot;, usage);&lt;br /&gt;
                return RETURN_WARN;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        unitnumber = atoi(argv[1]);&lt;br /&gt;
&lt;br /&gt;
        struct Library *IFFParseBase = IExec-&amp;gt;OpenLibrary(&amp;quot;iffparse.library&amp;quot;, 50);&lt;br /&gt;
        IIFFParse = (struct IFFParseIFace*)IExec-&amp;gt;GetInterface(IFFParseBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
        &lt;br /&gt;
        if (IIFFParse == NULL)&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open iff parsing library.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Allocate IFF_File structure.&lt;br /&gt;
         */&lt;br /&gt;
        if (!(iff = IIFFParse-&amp;gt;AllocIFF ()))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;AllocIFF() failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Set up IFF_File for Clipboard I/O.&lt;br /&gt;
         */&lt;br /&gt;
        if (!(iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard (unitnumber)))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Clipboard open failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Opened clipboard unit %ld\n&amp;quot;, unitnumber);&lt;br /&gt;
&lt;br /&gt;
        IIFFParse-&amp;gt;InitIFFasClip (iff);&lt;br /&gt;
&lt;br /&gt;
#ifdef WRITEREAD&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Start the IFF transaction.&lt;br /&gt;
         */&lt;br /&gt;
        if (error = IIFFParse-&amp;gt;OpenIFF (iff, IFFF_WRITE))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;OpenIFF for write failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Write our text to the clipboard as CHRS chunk in FORM FTXT&lt;br /&gt;
         *&lt;br /&gt;
         * First, write the FORM ID (FTXT)&lt;br /&gt;
         */&lt;br /&gt;
        if(!(error = IIFFParse-&amp;gt;PushChunk(iff, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN)))&lt;br /&gt;
                {&lt;br /&gt;
                /* Now the CHRS chunk ID followed by the chunk data&lt;br /&gt;
                 * We&#039;ll just write one CHRS chunk.&lt;br /&gt;
                 * You could write more chunks.&lt;br /&gt;
                 */&lt;br /&gt;
                if(!(error = IIFFParse-&amp;gt;PushChunk(iff, 0, ID_CHRS, IFFSIZE_UNKNOWN)))&lt;br /&gt;
                        {&lt;br /&gt;
                        /* Now the actual data (the text) */&lt;br /&gt;
                        textlen = strlen(mytext);&lt;br /&gt;
                        if(IIFFParse-&amp;gt;WriteChunkBytes(iff, mytext, textlen) != textlen)&lt;br /&gt;
                                {&lt;br /&gt;
                                IDOS-&amp;gt;Printf(&amp;quot;Error writing CHRS data.\n&amp;quot;);&lt;br /&gt;
                                error = IFFERR_WRITE;&lt;br /&gt;
                                }&lt;br /&gt;
                        }&lt;br /&gt;
                if(!error) error = IIFFParse-&amp;gt;PopChunk(iff);&lt;br /&gt;
                }&lt;br /&gt;
        if(!error) error = IIFFParse-&amp;gt;PopChunk(iff);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        if(error)&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;IFF write failed, error %ld: %s\n&amp;quot;,&lt;br /&gt;
                        error, errormsgs[-error - 1]);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Wrote text to clipboard as FTXT\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Now let&#039;s close it, then read it back&lt;br /&gt;
         * First close the write handle, then close the clipboard&lt;br /&gt;
         */&lt;br /&gt;
        IIFFParse-&amp;gt;CloseIFF(iff);&lt;br /&gt;
        if (iff-&amp;gt;iff_Stream) IIFFParse-&amp;gt;CloseClipboard ((struct ClipboardHandle *)&lt;br /&gt;
                                                iff-&amp;gt;iff_Stream);&lt;br /&gt;
&lt;br /&gt;
        if (!(iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard (unitnumber)))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Reopen of Clipboard failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Reopened clipboard unit %ld\n&amp;quot;, unitnumber);&lt;br /&gt;
&lt;br /&gt;
#endif /* WRITEREAD */&lt;br /&gt;
&lt;br /&gt;
        if (error = IIFFParse-&amp;gt;OpenIFF (iff, IFFF_READ))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;OpenIFF for read failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Tell iffparse we want to stop on FTXT CHRS chunks */&lt;br /&gt;
        if (error = IFFParse-&amp;gt;StopChunk(iff, ID_FTXT, ID_CHRS))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;StopChunk failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Find all of the FTXT CHRS chunks */&lt;br /&gt;
        while(1)&lt;br /&gt;
                {&lt;br /&gt;
                error = IIFFParse-&amp;gt;ParseIFF(iff,IFFPARSE_SCAN);&lt;br /&gt;
                if(error == IFFERR_EOC) continue;       /* enter next context */&lt;br /&gt;
                else if(error) break;&lt;br /&gt;
&lt;br /&gt;
                /* We only asked to stop at FTXT CHRS chunks&lt;br /&gt;
                 * If no error we&#039;ve hit a stop chunk&lt;br /&gt;
                 * Read the CHRS chunk data&lt;br /&gt;
                 */&lt;br /&gt;
                cn = IIFFParse-&amp;gt;CurrentChunk(iff);&lt;br /&gt;
&lt;br /&gt;
                if((cn)&amp;amp;&amp;amp;(cn-&amp;gt;cn_Type == ID_FTXT)&amp;amp;&amp;amp;(cn-&amp;gt;cn_ID == ID_CHRS))&lt;br /&gt;
                        {&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;CHRS chunk contains:\n&amp;quot;);&lt;br /&gt;
                        while((rlen = IIFFParse-&amp;gt;ReadChunkBytes(iff,readbuf,RBUFSZ)) &amp;gt; 0)&lt;br /&gt;
                                {&lt;br /&gt;
                                IDOS-&amp;gt;Write(Output(),readbuf,rlen);&lt;br /&gt;
                                }&lt;br /&gt;
                        if(rlen &amp;lt; 0)    error = rlen;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        if((error)&amp;amp;&amp;amp;(error != IFFERR_EOF))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf (&amp;quot;IFF read failed, error %ld: %s\n&amp;quot;,&lt;br /&gt;
                        error, errormsgs[-error - 1]);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
bye:&lt;br /&gt;
        if (iff) {&lt;br /&gt;
                /*&lt;br /&gt;
                 * Terminate the IFF transaction with the stream.  Free&lt;br /&gt;
                 * all associated structures.&lt;br /&gt;
                 */&lt;br /&gt;
                IIFFParse-&amp;gt;CloseIFF (iff);&lt;br /&gt;
&lt;br /&gt;
                /*&lt;br /&gt;
                 * Close the clipboard stream&lt;br /&gt;
                 */&lt;br /&gt;
                if (iff-&amp;gt;iff_Stream)&lt;br /&gt;
                                IIFFParse-&amp;gt;CloseClipboard ((struct ClipboardHandle *)&lt;br /&gt;
                                                iff-&amp;gt;iff_Stream);&lt;br /&gt;
                /*&lt;br /&gt;
                 * Free the IFF_File structure itself.&lt;br /&gt;
                 */&lt;br /&gt;
                IIFFParse-&amp;gt;FreeIFF (iff);&lt;br /&gt;
                }&lt;br /&gt;
        IExec-&amp;gt;DropInterface((struct Interface*)IIFFParse);&lt;br /&gt;
        IExec-&amp;gt;CloseLibrary (IFFParseBase);&lt;br /&gt;
&lt;br /&gt;
        return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== IFF Scanner Example ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Sift.c&amp;quot; lists the type and size of every chunk in an IFF file and, and checks the IFF file for correct syntax. You should use &amp;quot;Sift&amp;quot; to check IFF files created by your programs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 *&lt;br /&gt;
 * sift.c:       Takes any IFF file and tells you what&#039;s in it.  Verifies syntax and all that cool stuff.&lt;br /&gt;
 *&lt;br /&gt;
 * Usage: sift -c          ; For clipboard scanning&lt;br /&gt;
 *    or  sift &amp;lt;file&amp;gt;      ; For DOS file scanning&lt;br /&gt;
 *&lt;br /&gt;
 * Reads the specified stream and prints an IFFCheck-like listing of the contents of the IFF file, if any.&lt;br /&gt;
 * Stream is a DOS file for &amp;lt;file&amp;gt; argument, or is the clipboard&#039;s primary clip for -c.&lt;br /&gt;
 * This program must be run from a CLI.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MINARGS 2&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR usage = &amp;quot;Usage: sift IFFfilename (or -c for clipboard)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void PrintTopChunk(struct IFFHandle *);  /* prototype for our function */&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Text error messages for possible IFFERR_#? returns from various IFF routines.  To get the index into&lt;br /&gt;
 * this array, take your IFFERR code, negate it, and subtract one.&lt;br /&gt;
 *  idx = -error - 1;&lt;br /&gt;
 */&lt;br /&gt;
char    *errormsgs[] = {&lt;br /&gt;
        &amp;quot;End of file (not an error).&amp;quot;, &amp;quot;End of context (not an error).&amp;quot;, &amp;quot;No lexical scope.&amp;quot;,&lt;br /&gt;
        &amp;quot;Insufficient memory.&amp;quot;, &amp;quot;Stream read error.&amp;quot;, &amp;quot;Stream write error.&amp;quot;,&lt;br /&gt;
        &amp;quot;Stream seek error.&amp;quot;, &amp;quot;File is corrupt.&amp;quot;, &amp;quot;IFF syntax error.&amp;quot;,&lt;br /&gt;
        &amp;quot;Not an IFF file.&amp;quot;, &amp;quot;Required call-back hook missing.&amp;quot;, &amp;quot;Return to client.  You should never see this.&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct IFFParseIFace *IIFFParse = NULL;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    struct IFFHandle    *iff = NULL;&lt;br /&gt;
    int32                error;&lt;br /&gt;
    int16                cbio;&lt;br /&gt;
&lt;br /&gt;
        /* if not enough args or &#039;?&#039;, print usage */&lt;br /&gt;
        if(((argc)&amp;amp;&amp;amp;(argc&amp;lt;MINARGS))||(argv[argc-1][0]==&#039;?&#039;))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;%s\n&amp;quot;, usage);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Check to see if we are doing I/O to the Clipboard. */&lt;br /&gt;
        cbio = (argv[1][0] == &#039;-&#039;  &amp;amp;&amp;amp;  argv[1][1] == &#039;c&#039;);&lt;br /&gt;
&lt;br /&gt;
        struct Library *IFFParseBase = IExec-&amp;gt;OpenLibrary(&amp;quot;iffparse.library&amp;quot;, 50);&lt;br /&gt;
        IIFFParse = (struct IFFParseIFace*)IExec-&amp;gt;GetInterface(IFFParseBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
        if (IIFFParse == NULL)&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open iff parsing library.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Allocate IFF_File structure. */&lt;br /&gt;
        if (!(iff = IIFFParse-&amp;gt;AllocIFF()))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;AllocIFF() failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Internal support is provided for both AmigaDOS files, and the clipboard.device. This bizarre&lt;br /&gt;
         * &#039;if&#039; statement performs the appropriate machinations for each case.&lt;br /&gt;
         */&lt;br /&gt;
        if (cbio)&lt;br /&gt;
                {&lt;br /&gt;
                /*&lt;br /&gt;
                 * Set up IFF_File for Clipboard I/O.&lt;br /&gt;
                 */&lt;br /&gt;
                if (!(iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard(PRIMARY_CLIP)))&lt;br /&gt;
                        {&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;Clipboard open failed.\n&amp;quot;);&lt;br /&gt;
                        goto bye;&lt;br /&gt;
                        }&lt;br /&gt;
                IIFFParse-&amp;gt;InitIFFasClip(iff);&lt;br /&gt;
                }&lt;br /&gt;
        else&lt;br /&gt;
                {&lt;br /&gt;
                /* Set up IFF_File for AmigaDOS I/O.  */&lt;br /&gt;
                if (!(iff-&amp;gt;iff_Stream = IDOS-&amp;gt;Open(argv[1], MODE_OLDFILE)))&lt;br /&gt;
                        {&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;File open failed.\n&amp;quot;);&lt;br /&gt;
                        goto bye;&lt;br /&gt;
                        }&lt;br /&gt;
                IIFFParse-&amp;gt;InitIFFasDOS(iff);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Start the IFF transaction. */&lt;br /&gt;
        if (error = IIFFParse-&amp;gt;OpenIFF(iff, IFFF_READ))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;OpenIFF failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        while (1)&lt;br /&gt;
                {&lt;br /&gt;
                /*&lt;br /&gt;
                 * The interesting bit. IFFPARSE_RAWSTEP permits us to have precision monitoring of the&lt;br /&gt;
                 * parsing process, which is necessary if we wish to print the structure of an IFF file.&lt;br /&gt;
                 * ParseIFF() with _RAWSTEP will return the following things for the following reasons:&lt;br /&gt;
                 *&lt;br /&gt;
                 * Return code:                 Reason:&lt;br /&gt;
                 * 0                            Entered new context.&lt;br /&gt;
                 * IFFERR_EOC                   About to leave a context.&lt;br /&gt;
                 * IFFERR_EOF                   Encountered end-of-file.&lt;br /&gt;
                 * &amp;lt;anything else&amp;gt;              A parsing error.&lt;br /&gt;
                 */&lt;br /&gt;
                error = IIFFParse-&amp;gt;ParseIFF(iff, IFFPARSE_RAWSTEP);&lt;br /&gt;
&lt;br /&gt;
                /*&lt;br /&gt;
                 * Since we&#039;re only interested in when we enter a context, we &amp;quot;discard&amp;quot; end-of-context&lt;br /&gt;
                 * (_EOC) events.&lt;br /&gt;
                 */&lt;br /&gt;
                if (error == IFFERR_EOC)&lt;br /&gt;
                        continue;&lt;br /&gt;
                else if (error)&lt;br /&gt;
                        /*&lt;br /&gt;
                         * Leave the loop if there is any other error.&lt;br /&gt;
                         */&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                /* If we get here, error was zero. Print out the current state of affairs. */&lt;br /&gt;
                PrintTopChunk(iff);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * If error was IFFERR_EOF, then the parser encountered the end of&lt;br /&gt;
         * the file without problems. Otherwise, we print a diagnostic.&lt;br /&gt;
         */&lt;br /&gt;
        if (error == IFFERR_EOF)&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;File scan complete.\n&amp;quot;);&lt;br /&gt;
        else&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;File scan aborted, error %ld: %s\n&amp;quot;,&lt;br /&gt;
                        error, errormsgs[-error - 1]);&lt;br /&gt;
&lt;br /&gt;
bye:&lt;br /&gt;
        if (iff) {&lt;br /&gt;
                /* Terminate the IFF transaction with the stream. Free all associated structures. */&lt;br /&gt;
                IIFFParse-&amp;gt;CloseIFF(iff);&lt;br /&gt;
&lt;br /&gt;
                /*&lt;br /&gt;
                 * Close the stream itself.&lt;br /&gt;
                 */&lt;br /&gt;
                if (iff-&amp;gt;iff_Stream)&lt;br /&gt;
                        if (cbio)&lt;br /&gt;
                                IIFFParse-&amp;gt;CloseClipboard((struct ClipboardHandle *)iff-&amp;gt;iff_Stream);&lt;br /&gt;
                        else&lt;br /&gt;
                                IDOS-&amp;gt;Close(iff-&amp;gt;iff_Stream);&lt;br /&gt;
&lt;br /&gt;
                /* Free the IFF_File structure itself. */&lt;br /&gt;
                IIFFParse-&amp;gt;FreeIFF(iff);&lt;br /&gt;
                }&lt;br /&gt;
        IExec-&amp;gt;DropInterface((struct Interface*)IIFFParse);&lt;br /&gt;
        IExec-&amp;gt;CloseLibrary(IFFParseBase);&lt;br /&gt;
&lt;br /&gt;
        return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void PrintTopChunk(struct IFFHandle *iff)&lt;br /&gt;
{&lt;br /&gt;
        struct ContextNode      *top;&lt;br /&gt;
        int32                   i;&lt;br /&gt;
        char                    idbuf[5];&lt;br /&gt;
&lt;br /&gt;
        /* Get a pointer to the context node describing the current context. */&lt;br /&gt;
        if (!(top = IIFFParse-&amp;gt;CurrentChunk(iff)))&lt;br /&gt;
                return;&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Print a series of dots equivalent to the current nesting depth of chunks processed so far.&lt;br /&gt;
         * This will cause nested chunks to be printed out indented.&lt;br /&gt;
         */&lt;br /&gt;
        for (i = iff-&amp;gt;iff_Depth;  i--; )&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;. &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        /* Print out the current chunk&#039;s ID and size. */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;%s %ld &amp;quot;, IIFFParse-&amp;gt;IDtoStr(top-&amp;gt;cn_ID, idbuf), top-&amp;gt;cn_Size);&lt;br /&gt;
&lt;br /&gt;
        /* Print the current chunk&#039;s type, with a newline. */&lt;br /&gt;
        IDOS-&amp;gt;Printf(IIFFParse-&amp;gt;IDtoStr(top-&amp;gt;cn_Type, idbuf));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the IFFParse functions discussed in this article. Further information about these and other IFFParse functions can be found in the SDK.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AllocIFF()&lt;br /&gt;
| Creates an IFFHandle structure.&lt;br /&gt;
|-&lt;br /&gt;
| FreeIFF()&lt;br /&gt;
| Frees the IFFHandle structure created with AllocIFF().&lt;br /&gt;
|-&lt;br /&gt;
| OpenIFF()&lt;br /&gt;
| Initialize an IFFHandle structure to read or write an IFF stream.&lt;br /&gt;
|-&lt;br /&gt;
| CloseIFF()&lt;br /&gt;
| Closes an IFF context.&lt;br /&gt;
|-&lt;br /&gt;
| InitIFF()&lt;br /&gt;
| Initialize an IFFHandle as a user-defined stream.&lt;br /&gt;
|-&lt;br /&gt;
| InitIFFasDOS()&lt;br /&gt;
| Initialize an IFFHandle as an AmigaDOS stream.&lt;br /&gt;
|-&lt;br /&gt;
| InitIFFasClip()&lt;br /&gt;
| Initialize an IFFHandle as a clipboard stream.&lt;br /&gt;
|-&lt;br /&gt;
| OpenClipboard()&lt;br /&gt;
| Create a handle on a clipboard unit for InitIFFasClip().&lt;br /&gt;
|-&lt;br /&gt;
| ParseIFF()&lt;br /&gt;
| Parse an IFF file from an IFFHandle stream.&lt;br /&gt;
|-&lt;br /&gt;
| ReadChunkBytes()&lt;br /&gt;
| Read bytes from the current chunk into a buffer.&lt;br /&gt;
|-&lt;br /&gt;
| ReadChunkRecords()&lt;br /&gt;
| Read record elements from the current chunk into a buffer.&lt;br /&gt;
|-&lt;br /&gt;
| StopChunk()&lt;br /&gt;
| Declare a chunk that should cause ParseIFF() to return.&lt;br /&gt;
|-&lt;br /&gt;
| CurrentChunk()&lt;br /&gt;
| Get the context node for the current chunk.&lt;br /&gt;
|-&lt;br /&gt;
| PropChunk()&lt;br /&gt;
| Specify a property chunk to store.&lt;br /&gt;
|-&lt;br /&gt;
| FindProp()&lt;br /&gt;
| Search for a stored property in a given context.&lt;br /&gt;
|-&lt;br /&gt;
| CollectionChunk()&lt;br /&gt;
| Declare a chunk type for collection.&lt;br /&gt;
|-&lt;br /&gt;
| FindCollection()&lt;br /&gt;
| Get a pointer to the current list of collection items.&lt;br /&gt;
|-&lt;br /&gt;
| StopOnExit()&lt;br /&gt;
| Declare a stop condition for exiting a chunk.&lt;br /&gt;
|-&lt;br /&gt;
| EntryHandler()&lt;br /&gt;
| Add an entry handler to the IFFHandle context.&lt;br /&gt;
|-&lt;br /&gt;
| ExitHandler()&lt;br /&gt;
| Add an exit handler to the IFFHandle context.&lt;br /&gt;
|-&lt;br /&gt;
| PushChunk()&lt;br /&gt;
| Push a given context node onto the top of the context stack.&lt;br /&gt;
|-&lt;br /&gt;
| PopChunk()&lt;br /&gt;
| Pop the top context node off of the context stack.&lt;br /&gt;
|-&lt;br /&gt;
| CurrentChunk()&lt;br /&gt;
| Get the top context node for the current chunk.&lt;br /&gt;
|-&lt;br /&gt;
| ParentChunk()&lt;br /&gt;
| Get the nesting context node for a given chunk.&lt;br /&gt;
|-&lt;br /&gt;
| AllocLocalItem()&lt;br /&gt;
| Create a LocalContextItem (LCI) structure.&lt;br /&gt;
|-&lt;br /&gt;
| LocalItemData()&lt;br /&gt;
| Returns a pointer to the user data of a LocalContextItem (LCI).&lt;br /&gt;
|-&lt;br /&gt;
| StoreLocalItem()&lt;br /&gt;
| Insert a LocalContextItem (LCI).&lt;br /&gt;
|-&lt;br /&gt;
| StoreItemInContext()&lt;br /&gt;
| Store a LocalContextItem in a given context node.&lt;br /&gt;
|-&lt;br /&gt;
| FindPropContext()&lt;br /&gt;
| Find the property context for the current state.&lt;br /&gt;
|-&lt;br /&gt;
| FindLocalItem()&lt;br /&gt;
| Return a LocalContextItem from the context stack.&lt;br /&gt;
|-&lt;br /&gt;
| FreeLocalItem()&lt;br /&gt;
| Free a LocalContextItem (LCI) created with AllocLocalItem().&lt;br /&gt;
|-&lt;br /&gt;
| SetLocalItemPurge()&lt;br /&gt;
| Set purge vector for a local context item.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Fredrik Wikstrom</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Exec_Lists_and_Queues&amp;diff=9217</id>
		<title>Exec Lists and Queues</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Exec_Lists_and_Queues&amp;diff=9217"/>
		<updated>2017-09-21T17:29:55Z</updated>

		<summary type="html">&lt;p&gt;Fredrik Wikstrom: /* Scanning a List */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Exec Lists and Queues ==&lt;br /&gt;
&lt;br /&gt;
The Amiga system software operates in a dynamic environment of data structures. An early design goal of Exec was to keep the system flexible and open-ended by eliminating artificial boundaries on the number of system structures used. Rather than using static system tables, Exec uses dynamically created structures that are added and removed as needed. These structures can be put in an unordered &#039;&#039;list&#039;&#039;, or in an ordered &#039;&#039;list&#039;&#039; known as a &#039;&#039;queue&#039;&#039;. A list can be empty, but never full. This concept is central to the design of Exec. Understanding lists and queues is important to understanding not only Exec itself, but also the mechanism behind the Amiga&#039;s message and port based inter-process communication.&lt;br /&gt;
&lt;br /&gt;
Exec uses lists to maintain its internal database of system structures. Tasks, interrupts, libraries, devices, messages, I/O requests, and all other Exec data structures are supported and serviced through the consistent application of Exec&#039;s list mechanism. Lists have a common data structure, and a common set of functions is used for manipulating them. Because all of these structures are treated in a similar manner, only a small number of list handling functions need be supported by Exec.&lt;br /&gt;
&lt;br /&gt;
== List Structure ==&lt;br /&gt;
&lt;br /&gt;
A list is composed of a &#039;&#039;header&#039;&#039; and a doubly-linked chain of elements called &#039;&#039;nodes&#039;&#039;. The header contains memory pointers to the first and last nodes of the linked chain. The address of the header is used as the handle to the entire list. To manipulate a list, you must provide the address of its header.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig23-1.png|frame|center|Simplified Overview of an Exec List]]&lt;br /&gt;
&lt;br /&gt;
Nodes may be scattered anywhere in memory. Each node contains two pointers; a successor and a predecessor. As illustrated above, a list header contains two placeholder nodes that contain no data. In an empty list, the head and tail nodes point to each other.&lt;br /&gt;
&lt;br /&gt;
=== Node Structure Definition ===&lt;br /&gt;
&lt;br /&gt;
A Node structure is divided into three parts: linkage, information, and content. The linkage part contains memory pointers to the node&#039;s successor and predecessor nodes. The information part contains the node type, the priority, and a name pointer. The content part stores the actual data structure of interest. For nodes that require linkage only, a small MinNode structure is used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct MinNode&lt;br /&gt;
{&lt;br /&gt;
    struct MinNode *mln_Succ;&lt;br /&gt;
    struct MinNode *mln_Pred;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; mln_Succ&lt;br /&gt;
: points to the next node in the list (successor).&lt;br /&gt;
&lt;br /&gt;
; mln_Pred&lt;br /&gt;
: points to the previous node in the list (predecessor).&lt;br /&gt;
&lt;br /&gt;
When a type, priority, or name is required, a full-featured Node structure is used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Node&lt;br /&gt;
{&lt;br /&gt;
    struct Node *ln_Succ;&lt;br /&gt;
    struct Node *ln_Pred;&lt;br /&gt;
    uint8        ln_Type;&lt;br /&gt;
    int8         ln_Pri;&lt;br /&gt;
    STRPTR       ln_Name;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; ln_Type&lt;br /&gt;
: defines the type of the node (see &amp;amp;lt;exec/nodes.h&amp;amp;gt; for a complete list of types).&lt;br /&gt;
&lt;br /&gt;
; ln_Pri&lt;br /&gt;
: specifies the priority of the node, ranging from +127 (highest) to -128 (lowest).&lt;br /&gt;
&lt;br /&gt;
; ln_Name&lt;br /&gt;
: points to a printable name for the node (a NULL-terminated string).&lt;br /&gt;
&lt;br /&gt;
The Node and MinNode structures are often incorporated into larger structures, so groups of the larger structures can easily be linked together. For example, the Exec Interrupt structure is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Interrupt&lt;br /&gt;
{&lt;br /&gt;
    struct Node is_Node;&lt;br /&gt;
    APTR        is_Data;&lt;br /&gt;
    VOID        (*is_Code)();&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here the is_Data and is_Code fields represent the useful content of the node. Because the Interrupt structure begins with a Node structure, it may be passed to any of the Exec List manipulation functions.&lt;br /&gt;
&lt;br /&gt;
=== Node Initialization ===&lt;br /&gt;
&lt;br /&gt;
Before linking a node into a list, certain fields may need initialization. Initialization consists of setting the ln_Type, ln_Pri, and ln_Name fields to their appropriate values. (Note: this is only relevant to Node structures. The simpler MinNode structure does not have these fields.) The successor and predecessor fields do not require initialization.&lt;br /&gt;
&lt;br /&gt;
The ln_Type field contains the data type of the node. This indicates to Exec (and other subsystems) the type, and hence the structure, of the content portion of the node (the extra data after the Node structure). The standard system types are defined in the &amp;amp;lt;exec/nodes.h&amp;amp;gt; include file. Some examples of standard system types are NT_TASK, NT_INTERRUPT, NT_DEVICE, and NT_MSGPORT. Custom node types are allowed, and should be defined &#039;&#039;downwards&#039;&#039; from NT_USER, for example like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#define NT_MYTYPE1   NT_USER&lt;br /&gt;
#define NT_MYTYPE2   (NT_USER - 1)&lt;br /&gt;
#define NT_MYTYPE3   (NT_USER - 2)&lt;br /&gt;
/* etc., you get the idea */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ln_Pri field uses a signed numerical value ranging from +127 to -128 to indicate the priority of the node. Higher-priority nodes have greater values; for example, 127 is the highest priority, zero is nominal priority, and -128 is the lowest priority. Some Exec lists are kept sorted by priority order. In such lists, the highest-priority node is at the head of the list, and the lowest-priority node is at the tail of the list. Most Exec node types do not use a priority. In such cases, initialize the priority field to zero.&lt;br /&gt;
&lt;br /&gt;
The ln_Name field is a pointer to a NULL-terminated string of characters. Node names are used to find and identify list-bound objects (like public message ports and libraries), and to bind symbolic names to actual nodes. Names are also useful for debugging purposes, so it is a good idea to provide every node with a name. Take care to provide a valid name pointer; Exec does not copy name strings.&lt;br /&gt;
&lt;br /&gt;
This code fragment initializes a Node called &#039;&#039;sample.interrupt&#039;&#039;, an instance of the Interrupt data structure introduced above:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Interrupt interrupt;&lt;br /&gt;
&lt;br /&gt;
interrupt.is_Node.ln_Type = NT_INTERRUPT;&lt;br /&gt;
interrupt.is_Node.ln_Pri  = -10;&lt;br /&gt;
interrupt.is_Node.ln_Name = &amp;quot;sample.interrupt&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== List Header Structure Definition ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, a list header maintains memory pointers to the first and last nodes of the linked chain of nodes. It also serves as a handle for referencing the entire list. The minimum list header (&amp;quot;mlh_&amp;quot;) and the full-featured list header (&amp;quot;lh_&amp;quot;) are generally interchangeable.&lt;br /&gt;
&lt;br /&gt;
The structure MinList defines a minimum list header.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct MinList&lt;br /&gt;
{&lt;br /&gt;
    struct MinNode *mlh_Head;&lt;br /&gt;
    struct MinNode *mlh_Tail;&lt;br /&gt;
    struct MinNode *mlh_TailPred;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; mlh_Head&lt;br /&gt;
: points to the first node in the list.&lt;br /&gt;
&lt;br /&gt;
; mlh_Tail&lt;br /&gt;
: is always NULL.&lt;br /&gt;
&lt;br /&gt;
; mlh_TailPred&lt;br /&gt;
: points to the last node in the list.&lt;br /&gt;
&lt;br /&gt;
In a few limited cases a full-featured List structure will be required:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct List&lt;br /&gt;
{&lt;br /&gt;
    struct Node *lh_Head;&lt;br /&gt;
    struct Node *lh_Tail;&lt;br /&gt;
    struct Node *lh_TailPred;&lt;br /&gt;
    uint8        lh_Type;&lt;br /&gt;
    uint8        lh_Pad;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; lh_Type&lt;br /&gt;
: defines the type of nodes within the list (see &amp;amp;lt;exec/nodes.h&amp;amp;gt;).&lt;br /&gt;
&lt;br /&gt;
; lh_Pad&lt;br /&gt;
: is a structure alignment byte.&lt;br /&gt;
&lt;br /&gt;
One subtlety here must be explained further. The list header is constructed in an efficient, but confusing manner. Think of the header as a structure containing the head and tail nodes for the list. The head and tail nodes are placeholders, and never carry data. The head and tail portions of the header actually overlap in memory. lh_Head and lh_Tail form the head node; lh_Tail and lh_TailPred form the tail node. This makes it easy to find the start or end of the list, and eliminates any special cases for insertion or removal.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig23-2.png|frame|center|List Header Overlap]]&lt;br /&gt;
&lt;br /&gt;
The lh_Head and lh_Tail fields of the list header act like the ln_Succ and lh_Pred fields of a node. The lh_Tail field is set permanently to NULL, indicating that the head node is indeed the first on the list - that is, it has no predecessors. See the figure below.&lt;br /&gt;
&lt;br /&gt;
Likewise, the lh_Tail and lh_TailPred fields of the list header act like the ln_Succ and lh_Pred fields of a node. Here the NULL lh_Tail indicates that the tail node is indeed the last on the list-that is, it has no successors. See the figure below.&lt;br /&gt;
&lt;br /&gt;
=== Header Initialization ===&lt;br /&gt;
&lt;br /&gt;
List headers must be properly initialized before use. It is not adequate to initialize the entire header to zero. The head and tail entries must have specific values. The header must be initialized as follows:&lt;br /&gt;
&lt;br /&gt;
# Set the lh_Head field to the address of lh_Tail.&lt;br /&gt;
# Clear the lh_Tail field.&lt;br /&gt;
# Set the lh_TailPred field to the address of lh_Head.&lt;br /&gt;
# Set lh_Type to the same data type as the nodes to be kept the list. (Unless you are using a MinList).&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig23-3.png|frame|center|Initializing a List Header Structure]]&lt;br /&gt;
&lt;br /&gt;
The functions NewList() and NewMinList() are provided to properly initialize a List or a MinList, respectively, prior to use. This is not required if you create the List or MinList using AllocSysObject() because this function takes care of the initialization. The following call creates an empty Exec List of the NT_FONT type and initializes its header for you:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct List *newFontList;&lt;br /&gt;
&lt;br /&gt;
newFontList = (struct List *) IExec-&amp;gt;AllocSysObjectTags(ASOT_LIST,&lt;br /&gt;
                              ASOLIST_Type, NT_FONT,&lt;br /&gt;
                              TAG_END);&lt;br /&gt;
&lt;br /&gt;
if ( newFontList == NULL )&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Out of memory\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lists or MinLists created via AllocSysObject() must be disposed of using a corresponding FreeSysObject() call. Please note that the list must always be empty before disposal – FreeSysObject() does not handle node removal, so failing to free your list nodes will cause a memory leak!&lt;br /&gt;
&lt;br /&gt;
== List Functions ==&lt;br /&gt;
&lt;br /&gt;
Exec provides a number of symmetric functions for handling lists. There are functions for inserting and removing nodes, for adding and removing head and tail nodes, for inserting nodes in a priority order, and for searching for nodes by name.&lt;br /&gt;
&lt;br /&gt;
=== Node Insertion and Removal ===&lt;br /&gt;
&lt;br /&gt;
The Insert() function is used for inserting a new node into any position in a list. It always inserts the node following a specified node that is already part of the list. For example, Insert(header, node, pred) inserts the node node after the node pred in the specified list. If the pred node points to the list header or is NULL, the new node will be inserted at the head of the list. Similarly, if the pred node points to the lh_Tail of the list, the new node will be inserted at the tail of the list. However, both of these actions can be better accomplished with the functions mentioned in the &amp;quot;Special Case Insertion&amp;quot; section below.&lt;br /&gt;
&lt;br /&gt;
The Remove() function is used to remove a specified node from a list. For example, Remove(node) will remove the specified node from whatever list it is in. &#039;&#039;To be removed, a node must actually be in a list.&#039;&#039; If you attempt to remove a node that is not in a list, you will cause serious system problems.&lt;br /&gt;
&lt;br /&gt;
=== Special Case Insertion ===&lt;br /&gt;
&lt;br /&gt;
Although the Insert() function allows new nodes to be inserted at the head and the tail of a list, the AddHead() and AddTail() functions will do so with higher efficiency. Adding to the head or tail of a list is common practice in first-in-first-out (FIFO) or last-in-first-out (LIFO or stack) operations. For example, AddHead(header,node) would insert the node at the head of the specified list.&lt;br /&gt;
&lt;br /&gt;
=== Special Case Removal ===&lt;br /&gt;
&lt;br /&gt;
The two functions RemHead() and RemTail() are used in combination with AddHead() and AddTail() to create special list ordering. When you combine AddTail() and RemHead(), you produce a first-in-first-out (FIFO) list. When you combine AddHead() and RemHead() a last-in-first-out (LIFO or stack) list is produced. RemTail() exists for symmetry. Other combinations of these functions can also be used productively.&lt;br /&gt;
&lt;br /&gt;
Both RemHead() and RemTail() remove a node from the list, and return a pointer to the removed node. If the list is empty, the function returns a NULL result.&lt;br /&gt;
&lt;br /&gt;
=== MinList / MinNode Operations ===&lt;br /&gt;
&lt;br /&gt;
All of the above functions and macros will work with long or short format node structures. A MinNode structure contains only linkage information. A full Node structure contains linkage information, as well as type, priority and name fields. The smaller MinNode is used where space and memory alignment issues are important. The larger Node is used for queues or lists that require a name tag for each node.&lt;br /&gt;
&lt;br /&gt;
=== Prioritized Insertion ===&lt;br /&gt;
&lt;br /&gt;
The list functions discussed so far do not make use of the priority field in a Node. The Enqueue() function is equivalent to Insert(), except it inserts nodes into a list sorting them according to their priority. It keeps the higher-priority nodes towards the head of the list. All nodes passed to this function must have their priority and name assigned prior to the call. Enqueue(header,mynode) inserts mynode behind the lowest priority node with a priority greater than or equal to mynode&#039;s. For Enqueue() to work properly, the list must already be sort according to priority. Because the highest priority node is at the head of the list, the RemHead() function will remove the highest-priority node. Likewise, RemTail() will remove the lowest-priority node.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=FIFO Is Used For The Same Priority|text=If you add a node that has the same priority as another node in the queue, Enqueue() will use FIFO ordering. The new node is inserted following the last node of equal priority.}}&lt;br /&gt;
&lt;br /&gt;
=== Searching by Name ===&lt;br /&gt;
&lt;br /&gt;
Because many lists contain nodes with symbolic names attached (via the ln_Name field), it is possible to find a node by its name. This naming technique is used throughout Exec for such nodes as tasks, libraries, devices, and resources.&lt;br /&gt;
&lt;br /&gt;
The FindName() function searches a list for the first node with a given name. For example, FindName(header, &amp;amp;quot;Furrbol&amp;amp;quot;) returns a pointer to the first node named &amp;quot;Furrbol.&amp;quot; If no such node exists, a NULL is returned.&lt;br /&gt;
&lt;br /&gt;
The case of the name characters is significant; &amp;quot;foo&amp;quot; is different from &amp;quot;Foo.&amp;quot; If the case of a name should not be significant, use the FindIName() function.&lt;br /&gt;
&lt;br /&gt;
Quite naturally, you cannot use FindName() or FindIName() with MinLists because these do not support named nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig23-4.png|frame|center|Complete Sample List Showing all Interconnections]]&lt;br /&gt;
&lt;br /&gt;
=== More on the Use of Named Nodes ===&lt;br /&gt;
&lt;br /&gt;
To find multiple occurrences of nodes with identical names, the FindName() or FindIName() function is called multiple times. For example, if you want to find all the nodes with the name pointed to by name:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID DisplayName(struct List *list, CONST_STRPTR name)&lt;br /&gt;
{&lt;br /&gt;
struct Node *node;&lt;br /&gt;
&lt;br /&gt;
    if ( (node = IExec-&amp;gt;FindName(list, name)) )&lt;br /&gt;
        while ( node )&lt;br /&gt;
        {&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Found %s at location %lx\n&amp;quot;, node-&amp;gt;ln_Name, node);&lt;br /&gt;
            node = IExec-&amp;gt;FindName((struct List *)node, name);&lt;br /&gt;
        }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;No node with name %s found.\n&amp;quot;, name);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice that the second search uses the node found by the first search. The FindName() function &#039;&#039;never&#039;&#039; compares the specified name with that of the starting node. It always begins the search with the successor of the starting point.&lt;br /&gt;
&lt;br /&gt;
=== Empty Lists ===&lt;br /&gt;
&lt;br /&gt;
It is often important to determine if a list is empty. This can be done in many ways, but only two are worth mentioning. If either the lh_TailPred field is pointing to the list header or the ln_Succ field of the lh_Head is NULL, then the list is empty.&lt;br /&gt;
&lt;br /&gt;
These methods would be written as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* You can use this method...   */&lt;br /&gt;
if ( list-&amp;gt;lh_TailPred == (struct Node *)list )&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;list is empty\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* Or you can use this method   */&lt;br /&gt;
if ( NULL == list-&amp;gt;lh_Head-&amp;gt;ln_Succ )&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;list is empty\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Macros are available to make the code easier to read:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct List *list;&lt;br /&gt;
struct MinList *minlist;&lt;br /&gt;
&lt;br /&gt;
if ( IsListEmpty(list) )&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;list is empty\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if ( IsMinListEmpty(minlist) )&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;minlist is empty\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Scanning a List ===&lt;br /&gt;
&lt;br /&gt;
Occasionally a program may need to scan a list to locate a particular node, find a node that has a field with a particular value, or just print the list. Because lists are linked in both the forward and backward directions, the list can be scanned from either the head or tail.&lt;br /&gt;
&lt;br /&gt;
There are two methods to traverse lists in AmigaOS: 1) via direct manipulation of the pointers, and 2) using dedicated functions from Exec.&lt;br /&gt;
&lt;br /&gt;
Here is a code fragment that uses pointers and a &#039;&#039;for&#039;&#039; loop to print the names of all nodes in a list:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct List *list;&lt;br /&gt;
struct Node *node;&lt;br /&gt;
&lt;br /&gt;
for ( node = list-&amp;gt;lh_Head ; node-&amp;gt;ln_Succ != NULL ; node = node-&amp;gt;ln_Succ )&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;%p -&amp;gt; %s\n&amp;quot;, node, node-&amp;gt;ln_Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A common mistake is to process the head or tail nodes. Valid data nodes have non-NULL successor and predecessor pointers. The above loop exits when node-&amp;gt;ln_Succ is NULL.&lt;br /&gt;
&lt;br /&gt;
Another common mistake is to free a node from within a loop, then reference the free memory to obtain the next node pointer. An extra temporary pointer solves this second problem. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct List *list;&lt;br /&gt;
struct Node *node, *next;&lt;br /&gt;
&lt;br /&gt;
for ( node = list-&amp;gt;lh_Head; (next = node-&amp;gt;ln_Succ) != NULL ; node = next )&lt;br /&gt;
{&lt;br /&gt;
     /* Do something with the node. */&lt;br /&gt;
     /* This may include node removal from the list. */&lt;br /&gt;
     if ( some_condition )&lt;br /&gt;
     {&lt;br /&gt;
          IExec-&amp;gt;Remove(node);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here is a code fragment that uses functions and a &#039;&#039;for&#039;&#039; loop to print the names of all nodes in a list:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct List *list;&lt;br /&gt;
struct Node *node;&lt;br /&gt;
&lt;br /&gt;
for ( node = IExec-&amp;gt;GetHead(list) ; node != NULL ; node = IExec-&amp;gt;GetSucc(node) )&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;%p -&amp;gt; %s\n&amp;quot;, node, node-&amp;gt;ln_Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here is an example showing how to properly remove a node from a list while traversing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct List *list;&lt;br /&gt;
struct Node *node, *next;&lt;br /&gt;
&lt;br /&gt;
for ( node = IExec-&amp;gt;GetHead(list); node != NULL; node = next )&lt;br /&gt;
{&lt;br /&gt;
     next = IExec-&amp;gt;GetSucc(node);&lt;br /&gt;
     /* Do something with the node. */&lt;br /&gt;
     /* This may include node removal from the list. */&lt;br /&gt;
     if ( some_condition )&lt;br /&gt;
     {&lt;br /&gt;
          IExec-&amp;gt;Remove(node);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Programmers are free to choose the method they prefer. Performance is not likely a concern unless the code is used in a critical section of a program.&lt;br /&gt;
&lt;br /&gt;
=== Finding the List of a Node ===&lt;br /&gt;
&lt;br /&gt;
In an Exec List, the address of the header is used as the handle to the entire list so we can find the List given only a Node.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct List *findListOfNode(struct Node *node)&lt;br /&gt;
{&lt;br /&gt;
  struct List *head = NULL;&lt;br /&gt;
&lt;br /&gt;
  if ( node != NULL )&lt;br /&gt;
  {&lt;br /&gt;
    while ( node-&amp;gt;ln_Pred != NULL )&lt;br /&gt;
    {&lt;br /&gt;
      node = node-&amp;gt;ln_Pred;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    head = (struct List*)node;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return head;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Exec List Example ====&lt;br /&gt;
&lt;br /&gt;
The code below demonstrates the concepts and functions discussed in this section by building an application-defined Exec List.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// buildlist.c - example which uses an application-specific Exec list&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/lists.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/nodes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Our function prototypes */&lt;br /&gt;
VOID AddName(struct List *, CONST_STRPTR);&lt;br /&gt;
VOID FreeNameNodes(struct List *);&lt;br /&gt;
VOID DisplayNameList(struct List *);&lt;br /&gt;
VOID DisplayName(struct List *, CONST_STRPTR);&lt;br /&gt;
&lt;br /&gt;
struct NameNode {&lt;br /&gt;
    struct Node nn_Node;        /* System Node structure */&lt;br /&gt;
    TEXT   nn_Data[62];         /* Node-specific data */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define NAMENODE_ID   100       /* The type of &amp;quot;NameNode&amp;quot; */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct List *nameList;&lt;br /&gt;
&lt;br /&gt;
    nameList = (struct List *) IExec-&amp;gt;AllocSysObjectTags(ASOT_LIST, TAG_END);&lt;br /&gt;
    &lt;br /&gt;
    if ( nameList == NULL )&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Out of memory\n&amp;quot;);&lt;br /&gt;
    else {&lt;br /&gt;
        AddName(nameList,&amp;quot;Name7&amp;quot;);   AddName(nameList,&amp;quot;Name6&amp;quot;);&lt;br /&gt;
        AddName(nameList,&amp;quot;Name5&amp;quot;);   AddName(nameList,&amp;quot;Name4&amp;quot;);&lt;br /&gt;
        AddName(nameList,&amp;quot;Name2&amp;quot;);   AddName(nameList,&amp;quot;Name0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        AddName(nameList,&amp;quot;Name7&amp;quot;);   AddName(nameList,&amp;quot;Name5&amp;quot;);&lt;br /&gt;
        AddName(nameList,&amp;quot;Name3&amp;quot;);   AddName(nameList,&amp;quot;Name1&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        DisplayName(nameList,&amp;quot;Name5&amp;quot;);&lt;br /&gt;
        DisplayNameList(nameList);&lt;br /&gt;
&lt;br /&gt;
        FreeNameNodes(nameList);&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_LIST, nameList);  /* Free list header */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Allocate a NameNode structure, copy the given name into the structure,&lt;br /&gt;
 * then add it the specified list.  This example does not provide an&lt;br /&gt;
 * error return for the out of memory condition.&lt;br /&gt;
*/&lt;br /&gt;
VOID AddName(struct List *list, CONST_STRPTR name)&lt;br /&gt;
{&lt;br /&gt;
    struct NameNode *nameNode = IExec-&amp;gt;AllocSysObjectTags(ASOT_NODE,&lt;br /&gt;
           ASONODE_Size, sizeof(struct NameNode),&lt;br /&gt;
           ASONODE_Type, NAMENODE_ID,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if ( nameNode == NULL )&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Out of memory\n&amp;quot;);&lt;br /&gt;
    else {&lt;br /&gt;
        IUtility-&amp;gt;Strlcpy(nameNode-&amp;gt;nn_Data, name, sizeof(nameNode-&amp;gt;nn_Data));&lt;br /&gt;
        nameNode-&amp;gt;nn_Node.ln_Name = nameNode-&amp;gt;nn_Data;&lt;br /&gt;
        IExec-&amp;gt;AddHead((struct List *)list, (struct Node *)nameNode);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Free the entire list, including the header.  The header is not updated&lt;br /&gt;
 * as the list is freed. This function demonstrates how to avoid&lt;br /&gt;
 * referencing freed memory when deallocating nodes.&lt;br /&gt;
 */&lt;br /&gt;
VOID FreeNameNodes(struct List *list)&lt;br /&gt;
{&lt;br /&gt;
    struct NameNode *workNode;&lt;br /&gt;
    struct NameNode *nextNode;&lt;br /&gt;
&lt;br /&gt;
    workNode = (struct NameNode *) IExec-&amp;gt;GetHead(list);  /* First node */&lt;br /&gt;
    while ( (nextNode = (struct NameNode *) IExec-&amp;gt;GetSucc((struct Node *)workNode)) ) {&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_NODE, workNode);&lt;br /&gt;
        workNode = nextNode;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Print the names of each node in a list.&lt;br /&gt;
 */&lt;br /&gt;
VOID DisplayNameList(struct List *list)&lt;br /&gt;
{&lt;br /&gt;
    struct Node *node;&lt;br /&gt;
&lt;br /&gt;
    if ( IsListEmpty(list) )&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;List is empty.\n&amp;quot;);&lt;br /&gt;
    else {&lt;br /&gt;
        for ( node = IExec-&amp;gt;GetHead(list); node != NULL; node = IExec-&amp;gt;GetSucc(node) )&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;%lx -&amp;gt; %s\n&amp;quot;, node, node-&amp;gt;ln_Name);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Print the location of all nodes with a specified name.&lt;br /&gt;
 */&lt;br /&gt;
VOID DisplayName(struct List *list, CONST_STRPTR name)&lt;br /&gt;
{&lt;br /&gt;
    struct Node *node;&lt;br /&gt;
&lt;br /&gt;
    if ( (node = IExec-&amp;gt;FindName(list, name)) ) {&lt;br /&gt;
        while ( node ) {&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Found a %s at location %lx\n&amp;quot;, node-&amp;gt;ln_Name, node);&lt;br /&gt;
            node = IExec-&amp;gt;FindName((struct List *)node, name);&lt;br /&gt;
        }&lt;br /&gt;
    } else IDOS-&amp;gt;Printf(&amp;quot;No node with name %s found.\n&amp;quot;, name);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Important Note About Shared Lists ===&lt;br /&gt;
&lt;br /&gt;
It is possible to run into contention problems with other tasks when manipulating a list that is shared by more than one task. &#039;&#039;None&#039;&#039; of the standard Exec list functions arbitrates for access to the list. For example, if some other task happens to be modifying a list while your task scans it, an inconsistent view of the list may be formed. This can result in a corrupted system.&lt;br /&gt;
&lt;br /&gt;
Generally it is not permissible to read or write a shared list without first locking out access from other tasks. &#039;&#039;All&#039;&#039; users of a list must use the same arbitration method. Several arbitration techniques are used on the Amiga. Some lists are protected by a semaphore. The ObtainSemaphore() call grants ownership of the list (see the [[Exec_Semaphores|Exec Semaphores]] for more information). Some lists require special arbitration. For example, you must use the Intuition LockIBase(0) call before accessing any Intuition lists. Other lists may be accessed only during Forbid() or Disable() (see [[Exec_Tasks|Exec Tasks]] for more information).&lt;br /&gt;
&lt;br /&gt;
The preferred method for arbitrating use of a shared list is through semaphores because a semaphores only holds off other tasks that are trying to access the shared list. Rather than suspending all multitasking. Failure to lock a shared list before use &#039;&#039;will&#039;&#039; result in unreliable operation.&lt;br /&gt;
&lt;br /&gt;
Note that I/O functions including printf() generally call Wait() to wait for I/O completion, and this allows other tasks to run. Therefore, it is not safe to print or Wait() while traversing a list unless the list is fully controlled by your application, or if the list is otherwise guaranteed not to change during multitasking.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following table gives a brief description of the Exec list and queue functions and macros. See the SDK for details about each call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Exec Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AddHead()&lt;br /&gt;
| Insert a node at the head of a list.&lt;br /&gt;
|-&lt;br /&gt;
| AddTail()&lt;br /&gt;
| Append a node to the tail of a list.&lt;br /&gt;
|-&lt;br /&gt;
| AllocSysObject(ASOT_LIST)&lt;br /&gt;
| Allocate and initialize a new List or MinList.&lt;br /&gt;
|-&lt;br /&gt;
| AllocSysObject(ASOT_NODE)&lt;br /&gt;
| Allocate and initialize a new list node.&lt;br /&gt;
|-&lt;br /&gt;
| Enqueue()&lt;br /&gt;
| Insert or append a node to a system queue.&lt;br /&gt;
|-&lt;br /&gt;
| FindIName()&lt;br /&gt;
| Find a node with a given name in a system list ignoring case.&lt;br /&gt;
|-&lt;br /&gt;
| FindName()&lt;br /&gt;
| Find a node with a given name in a system list.&lt;br /&gt;
|-&lt;br /&gt;
| FreeSysObject(ASOT_LIST)&lt;br /&gt;
| Free an allocated List or MinList.&lt;br /&gt;
|-&lt;br /&gt;
| FreeSysObject(ASOT_NODE)&lt;br /&gt;
| Free an allocated list node.&lt;br /&gt;
|-&lt;br /&gt;
| GetHead()&lt;br /&gt;
| Gets the head node of a list.&lt;br /&gt;
|-&lt;br /&gt;
| GetPred()&lt;br /&gt;
| Gets the node predecessor.&lt;br /&gt;
|-&lt;br /&gt;
| GetSucc()&lt;br /&gt;
| Gets the node successor.&lt;br /&gt;
|-&lt;br /&gt;
| GetTail()&lt;br /&gt;
| Gets the tail node of a list.&lt;br /&gt;
|-&lt;br /&gt;
| Insert()&lt;br /&gt;
| Insert a node into a list.&lt;br /&gt;
|-&lt;br /&gt;
| IsMinListEmpty&lt;br /&gt;
| Test if minimal list is empty&lt;br /&gt;
|-&lt;br /&gt;
| IsListEmpty&lt;br /&gt;
| Test if list is empty&lt;br /&gt;
|-&lt;br /&gt;
| NewMinList()&lt;br /&gt;
| Initialize a minimal list structure for use.&lt;br /&gt;
|-&lt;br /&gt;
| NewList()&lt;br /&gt;
| Initialize a list structure for use.&lt;br /&gt;
|-&lt;br /&gt;
| MoveList()&lt;br /&gt;
| Moves all the nodes from one list to another efficiently.&lt;br /&gt;
|-&lt;br /&gt;
| RemHead()&lt;br /&gt;
| Remove the head node from a list.&lt;br /&gt;
|-&lt;br /&gt;
| Remove()&lt;br /&gt;
| Remove a node from a list.&lt;br /&gt;
|-&lt;br /&gt;
| RemTail()&lt;br /&gt;
| Remove the tail node from a list.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Fredrik Wikstrom</name></author>
	</entry>
</feed>