eXML

Introduction and Overview

eXML is a RISC OS module which adds support for the XML file format to applications written in any language, including BBC BASIC. Existing ports of XML libraries have generally been C sources written by others, optimised for the C language, and not straightforward to call from other languages; some have also been withdrawn by their authors. eXML exists to give RISC OS software a single, dependable set of SWI calls for creating and parsing XML documents, without requiring a C compiler or a callback-based programming style.

Only a subset of the full XML specification is implemented. The intention is to extend coverage over time, but this is not guaranteed, and several calls described in this chapter are only partially implemented or not yet implemented at all; each such call says so explicitly.

Approach

Most existing XML libraries rely heavily on callbacks and handlers: the main program registers functions that the parser calls when it recognises something of interest. This is a natural style in C or C++, but it does not suit simpler languages such as BBC BASIC.

eXML instead follows the same pattern as the RISC OS window manager. The application calls SWI eXML_Parse with the appropriate parameters, and the call exits with a return code describing what was found. Once the application has dealt with that return code, it calls SWI eXML_Parse again, and repeats this until it receives the Finished code, at which point it resumes the normal flow of the program.

When handling long XML files, it may be desirable to call Wimp_Poll frequently from within the parsing loop. Take care when doing this: another running task may not be aware that your program is still in the middle of parsing a document.

Encodings

Several character encodings can be used for the data in a document. The eXML calls refer to these encodings by a fixed number:

NumberEncoding
0UTF-8
1UTF-16
2ISO-8859-1 (Latin-1)
3US-ASCII
4MacRoman
5Big5

Not every listed encoding is necessarily implemented in the current release of the module: selecting an unsupported encoding produces the UnknownEncoding result code from SWI eXML_Parse. Until wider encoding support is confirmed, use the standard Latin-1 encoding (2) for documents. UTF-8, in particular, though ASCII-compatible and defined in Unicode, ISO 10646-1 and RFC 2279, is not currently supported.

Reserved characters are escaped transparently on both read and write, so calling code does not need to handle this itself:

CharacterEscaped form
< (less than)&lt;
> (greater than)&gt;
&amp; (ampersand)&amp;
' (apostrophe)&apos;
" (quote)&quot;

Workspace and the element stack

The workspace used by the eXML module must be supplied by the calling application, rather than being owned by the module itself. This allows the module to be replaced by a newer version at any time without disturbing running applications, and allows several programs (or several independent parts of one program) to run concurrently, each with its own workspace and its own preferences. For example, a C or Assembler program expects strings to be terminated with a 0x00 byte, whereas BBC BASIC expects a 0x0D (Carriage Return) terminator; because each caller supplies its own workspace, each can select the terminator it needs without affecting any other caller.

Within the supplied workspace, eXML keeps a stack of currently open elements, fixed at 1024 bytes. Each element that is opened is pushed onto this stack as a null-terminated string, and the module maintains pointers to the current element. This lets the module track the order in which elements are opened and closed, report an error if it finds an inconsistency (for example, elements closed out of order), and, when writing, close any elements you have left open without you having to name them individually.

The module name 'eXML'

The module is called eXML, pronounced "ex-em-el", in the same way as the abbreviation XML. The extra leading e exists because SWI names cannot begin with the letter X: that letter is reserved as a prefix which suppresses a SWI's normal error handling. To trap errors from an eXML call yourself, prefix its name with X in the usual way, for example SYS "XeXML_StartElement", ....

Library

A BBC BASIC library is supplied in the !eXML application folder. Place this folder somewhere the Filer sees at start-up, such as inside !Boot.Resources, so that programs using the library can find it.

Technical Details

eXML offers a set of SWI calls that any application can use to create and parse XML files. Because the module can be dynamically replaced, and each caller supplies its own workspace, each application (or each routine within an application) can hold its own set of preferences to be honoured by eXML independently of any other caller.

Before eXML can be used, the application must reserve a block of memory of suitable size for the workspace. The application requests the required size by calling SWI eXML_InitBuffer with a null pointer, then calls it again with a pointer to the newly claimed memory to initialise it to sensible defaults. Preferences are then set with SWI eXML_SetOption; for example, textual strings returned by the module are terminated with a 0x00 byte by default, which suits C or Assembler, but BASIC needs a 0x0D (Carriage Return) terminator instead, and SWI eXML_SetOption is used to change this.

The available calls fall into four groups, described in the four SWI Calls sections below: calls to initialise the XML workspace, calls to read (parse) an XML file, calls to write an XML file, and universal calls that are not specifically tied to XML parsing or writing.

Data structures

Attribute array

When SWI eXML_Parse reports a StartElement or Declaration, or when supplying attributes to SWI eXML_Open, SWI eXML_StartElement, or SWI eXML_Declaration, attributes are held as an array of pairs of pointers: a pointer to the attribute's name, followed by a pointer to its value. The array is terminated by a null pointer. For example, the element

<frame id="1" name="time and date">

produces the following attribute array:

OffsetSizeContents
04pointer to "id"
44pointer to "1"
84pointer to "name"
124pointer to "time and date"
164Reserved, must be zero

A pointer to an attribute array is 0 (rather than a valid pointer to an empty array) whenever no attributes were found, or none are to be supplied.

Context array and context string

On exit from SWI eXML_Parse, R3 normally points to a zero-terminated array of word pointers, one per currently open element, in the order the elements were opened - the path from the document root down to (but not including) the element currently being processed. If, for example, an element is opened while the parser is at document/chapter/page, the context array on exit looks like this:

OffsetSizeContents
04pointer to "document"
44pointer to "chapter"
84pointer to "page"
124Reserved, must be zero

If bit 0 of the flags word is set on entry to SWI eXML_Parse, R3 instead points to a single context string using the current element separator (see Set the character used to separate element names in a context string), for example /document/chapter/page, rather than to an array of pointers. The name of the element currently being opened or closed is not included in either form of context, because at that point it is not yet (or is no longer) part of the context; append it yourself if you need the full path, eg. context$ = context$ + "/" + $element%. SWI eXML_Context produces the string form of the context on demand, at any point, without needing bit 0 of the flags word to have been set.

Colour data block

SWI eXML_ColourOp reads and writes colour values through a fixed-size, fixed-format data block held in memory supplied by the caller - either a short-lived block used purely for one conversion, or part of a larger structure holding several colours or other parameters. Always request the required size first (see Initialise a colour data block, or find its required size), since it may grow in a future version of the module.

OffsetSizeFieldContents
04FlagsBit flags; not used at present
44ModelThe original colour model used (RGB, CMYK, HSV, YUV); see below
84FormatThe original format (syntax) used to define the colour; see below
124ValueThe colour, always as a RISC OS colour word (see below)
164OriginalThe colour in its original colour model
2032NameName of the colour, if it was a named colour, including terminator

The Model field is stored purely for reference, for example so that your software can preselect an appropriate colour model in a colour picker matching how the colour was originally specified.

The colour Value is always held as a RISC OS colour word, &BBGGRRtt: one byte each for blue, green and red, and a trailing transparency byte (0 for fully opaque, 255 for fully transparent).

Colour model numbers:

NumberModel
1RGB (Red, Green, Blue)
2CMYK (Cyan, Magenta, Yellow, Key/black)
3HSV (Hue, Saturation, Value)
4YUV (Luminance, B-Y component, R-Y component)

Colour format numbers, used both to report the format a colour was read in and to select the format written by Write a colour data block out as a formatted text string:

NumberFormat
0Default - use the format already recorded in the data block
1RISCOS - &BBGGRRtt, eg. &FF13D400
2RGB - rgb(red,green,blue), values 0-255
3RGB% - rgb(red%,green%,blue%), values 0%-100%
4FullHex - #RRGGBB
5ShortHex - #RGB (4096 colours)
6CMYK - cmyk(cyan,magenta,yellow,key), values 0-255
7CMYK% - cmyk(cyan%,magenta%,yellow%,key%)
8HSL - hsl(hue,saturation%,lightness%)
9HSV - hsv(hue,saturation,value), values 0-255
10HSV% - hsv(hue,saturation%,value%)
11Name - a named HTML colour, eg. white, yellow, green
12YUV - not yet implemented
13YUV% - not yet implemented
14Simple - red,green,blue[,transparency] with no prefix

The full set of named HTML colours recognised is: white, yellow, orange, red, fuchsia, silver, gray, olive, purple, maroon, aqua, lime, teal, green, blue, navy, black. Names are not case-sensitive, and their use is discouraged in favour of an explicit RGB value wherever possible.

SWI Calls: Initialising the XML Workspace

The SWI calls are grouped, across this section and the three that follow it, as they are in the original specification: calls to initialise the XML workspace, calls to read (parse) an XML file, calls to write an XML file, and universal calls used by both directions (or unrelated to parsing altogether).

eXML_InitBufferSWI &576d8
Reserve and initialise the XML workspace, with a configurable Content buffer
R0=pointer to workspace in the application's own memory, or 0 to request the required size
R1=required size, in bytes, of the Content buffer
R5=flags; 0 for now
R0=required size of the workspace, in bytes (when R0 was 0 on entry)
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Call this SWI twice. First, with a null pointer in R0, to discover the required workspace size; then, having claimed a block of that size, call it again with a pointer to that block in R0. The second call clears the block to zero and fills in sensible default preferences. The resulting pointer (R0 on the second call) must be passed in R0 to every other eXML SWI for that session.

R1 gives the required size of the Content buffer used to hold parsed content data; the module imposes a minimum of 1KB if a smaller value (or a null pointer) is supplied.

This call supersedes SWI eXML_Init and should be used in its place.

content_size% = 10*1024
SYS "eXML_InitBuffer",0,content_size%,,,,0 TO size%
DIM xml% size%
SYS "eXML_InitBuffer",xml%,content_size%

eXML_InitSWI &576c0
Deprecated: reserve and initialise the XML workspace with a fixed-size Content buffer
R0=pointer to workspace in the application's own memory, or 0 to request the required size
R0=required size of the workspace, in bytes (when R0 was 0 on entry)
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

This call is deprecated, and is supplied only for backwards compatibility. It assumes a fixed size for the Content buffer (10KB from version 0.18 onwards; 1KB in older versions). SWI eXML_InitBuffer allows the Content buffer size to be chosen and should be used in preference to this call.

As with SWI eXML_InitBuffer, call this SWI twice: once with a null pointer in R0 to discover the required size, and once with a pointer to a claimed block of that size to initialise it.

SYS "eXML_Init" TO size%
DIM xml% size%
FOR dummy% = 0 TO size% STEP 4
   xml%!dummy% = 0
NEXT
SYS "eXML_Init",xml%

eXML_SetOptionSWI &576c1
Set a configurable preference for a workspace
R0=pointer to workspace
R1=

Variable to set:

VariableMeaning
0Set the number of spaces used to indent nested elements
1Set the depth up to which extra blank lines are inserted before elements
2Set the character used to terminate strings returned by the module
3Set the character used to separate element names in a context string
4Set the character used to separate an element's context from an attribute name
5Set the RISC OS filetype used for files created by the module
6Set the directory used for the temporary file created when updating an XML file
R2=new value, or pointer to a string, as appropriate to the selected variable
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is re-entrant

Once initialised, eXML uses sensible default values for every configurable preference. This call changes one of them for the workspace given in R0. The variable number in R1 behaves as a reason code, selecting which preference is changed and how R2 is interpreted; see each reason below.

eXML_SetOption 0IndentSWI &576c1
Set the number of spaces used to indent nested elements
R2=number of spaces to indent by, or 0 to disable indenting
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

To produce readable XML output, eXML indents nested elements. This preference sets the number of spaces used per nesting level; the default is 2. Setting it to 0 disables indenting entirely.

SYS "eXML_SetOption",xml%,0,2

<?xml version="1.0" encoding="ISO-8859-1"?>
<page type="Page" category="Test" name="1" fileType="CNDoc">
  <begin>
    <transition name="Dissolve">
      <parameters>
        <speed>Fast</speed>
      </parameters>
    </transition>
  </begin>
</page>

eXML_SetOption 1ExtraLFSWI &576c1
Set the depth up to which extra blank lines are inserted before elements
R2=deepest nesting level, counting from 1, at which an extra blank line is inserted before an element; 0 to disable
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

Readability can be further improved by inserting a blank line before the opening tag of higher-level elements. By default, extra lines are only inserted at the first nesting level; setting this to 2 or 3 extends that to deeper levels.

eXML_SetOption 2TerminatorSWI &576c1
Set the character used to terminate strings returned by the module
R2=ASCII value of the terminating character
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

By default, every string returned by eXML is terminated with a 0x00 byte, suiting C and Assembler. BASIC programs need a 0x0D (Carriage Return) terminator instead; use this preference to change it. The change affects only later calls made with this workspace pointer in R0.

SYS "eXML_SetOption",xml%,2,13

eXML_SetOption 3ElementSeparatorSWI &576c1
Set the character used to separate element names in a context string
R2=ASCII value of the separator character, or a string whose first character is used
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

SWI eXML_Context, and the string form of context returned by SWI eXML_Parse, join element names with / by default, eg. /document/chapter/page/frame. This preference selects a different separator character. Avoid choosing a character that can legally appear inside an element name.

SYS "eXML_SetOption",xml%,3,36
SYS "eXML_SetOption",xml%,3,"$"

eXML_SetOption 4AttributeSeparatorSWI &576c1
Set the character used to separate an element's context from an attribute name
R2=ASCII value of the separator character, or a string whose first character is used
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

This preference works like Set the character used to separate element names in a context string, but for addressing an attribute within an element's opening tag, eg. /document/chapter/page@print to address the print attribute of that page element, once @ has been set as the attribute separator.

eXML_SetOption 5FileTypeSWI &576c1
Set the RISC OS filetype used for files created by the module
R2=RISC OS filetype number
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

By default, XML files created by eXML are typed as Text (&FFF), so they can be checked in a standard text editor such as !Edit, !StrongEd or !Zap. This preference sets an alternative filetype, such as &F80 (XML), to be applied when the file is closed. It only affects files being created; it is ignored when reading an existing file.

SYS "eXML_SetOption",xml%,5,&F80

eXML_SetOption 6TempPathSWI &576c1
Set the directory used for the temporary file created when updating an XML file
R2=pointer to a directory path string
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

SWI eXML_UpdateFile may need to create a temporary file. By default, eXML uses <Wimp$ScrapDir> (creating an eXML subdirectory within it), falling back to <eXML$Dir>.Scrap if that variable cannot be accessed. This preference selects a different directory.

SYS "eXML_SetOption",xml%,6,"<MyApp$Dir>.Files.Temp"

eXML_ClearAttributesSWI &576d5
Clear the pending attribute stack
R0=pointer to workspace
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is re-entrant

Clears the stack of attributes accumulated by SWI eXML_Attribute. The stack is cleared automatically once a new element is created, so this call is rarely needed; it exists for the case where the state of the stack is uncertain, for example after an error.

SWI Calls: Reading (Parsing) an XML File

eXML_ParseBufferSWI &576c2
Present an XML document already held in memory to the parser
R0=pointer to workspace
R1=pointer to the XML data
R2=size of the data, in bytes
R5=flags; 0 for now
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

The simplest way to parse an XML file is to load it entirely into a buffer in the application's own memory and hand that buffer to eXML with this call, then parse it with repeated calls to SWI eXML_Parse. This only works when the file's size is known and it is small enough to hold in memory, which is true of most everyday XML files.

SYS "OS_File",17,"adfs::4.$.TestFile" TO type%,,,,xml_len%
IF type% = 0 THEN ERROR 1,"No such file"
DIM xml_buf% xml_len%
OSCLI "LOAD "+xml$+" "+STR$~(xml_buf%)
SYS "eXML_ParseBuffer",xml%,xml_buf%,xml_len%

eXML_ParseFileSWI &576c3
Open an XML file to be read, a character at a time, by the parser
R0=pointer to workspace
R1=pointer to the filename
R5= Flags:
Bit(s)NameMeaning
3NoHeaderClear:A leading <?xml?> header is required
Set:Accept a file without a proper <?xml?> header; only the first character of the file is then checked, and must be a "<"
R0=handle of the opened file, or 0 if it could not be opened
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Use this call instead of SWI eXML_ParseBuffer when a file is too large to hold in memory, or when its size is not known in advance, such as when parsing streamed data. The file is read a character at a time as parsing proceeds. As with SWI eXML_ParseBuffer, call SWI eXML_Parse repeatedly until parsing is finished.

SYS "eXML_InitBuffer" TO size%
DIM xml% size%
SYS "eXML_InitBuffer",xml%

SYS "eXML_SetOption",xml%,eXML_Option_Terminator,13
SYS "eXML_SetOption",xml%,eXML_Option_Separator,"/"

SYS "eXML_ParseFile",xml%,"adfs::4.$.MyFile" TO in%

REPEAT
   SYS "eXML_Parse",xml%,0 TO r%,element%,attr%,context%
   CASE r% OF
      WHEN eXML_Parser_Start
         PRINT "S/"+$element%
      WHEN eXML_Parser_End
         PRINT "E/"+$element%
      WHEN eXML_Parser_ERROR
         PRINT "ERROR !!!"
   ENDCASE
UNTIL r% = eXML_Parser_Finished OR r% = eXML_Parser_ERROR

SYS "eXML_Close",xml%

eXML_ParseSWI &576c4
Continue parsing an XML document, one syntactic event at a time
R1=mask; not used at present, should be 0
R5= Flags:
Bit(s)NameMeaning
0ContextClear:Return a context array in R3 on exit (see context array)
Set:Return a context string in R3 on exit (see context string)
1NoErrorClear:Report an error for badly structured XML
Set:Don't escape special characters, and don't produce an error for badly structured input; useful when parsing simple HTML, which may nest elements improperly, but should never be set when parsing genuine XML
R0=

Return code:

CodeMeaning
0Null - no event to report; call Wimp_Poll and call this SWI again, to allow multitasking to continue whilst parsing a large file
1Start - a StartElement was found (R1 -> element name)
2End - an EndElement was found (R1 -> element name)
3Content - content was found (R1 -> content, R2 = number of bytes to read)
4Declaration - a declaration was found, eg. <?xat creator="MyProg" version="4.0"?> (R1 -> declaration name)
5PI - a Processing Instruction was found
6Entity
7Comment - a comment was found
8CDATA
9DTD - a DOCTYPE definition was found; see SWI eXML_DecodeDTD (R1 -> undecoded DTD data)
10Finished - parsing has finished (from version 0.22 onwards, R1 = number of bytes read)
11ERROR - an error occurred during parsing (R1 = row, R2 = column of the last element encountered before the error)
12Encoding - the requested encoding is not known or not supported
R1=pointer to the current element name, for the codes noted above; terminated with the configured terminator (see Set the character used to terminate strings returned by the module)
R2=pointer to an attribute array, or 0 if there are no attributes, for Start and Declaration
R3=pointer to a context array or context string, depending on bit 0 of the flags on entry
Interrupts are undefined
Fast interrupts are enabled
Processor is in SVC mode
SWI is not re-entrant

Once the workspace has been initialised and a document has been presented with SWI eXML_ParseBuffer or SWI eXML_ParseFile, call this SWI repeatedly to parse it, until it returns the Finished or ERROR code. eXML tracks the exact position reached within the document; to restart parsing from the beginning, present the document again with SWI eXML_ParseBuffer or SWI eXML_ParseFile.

Your application need not respond to every return code - many can safely be ignored - but it must always respond to ERROR and Finished, so that the parsing loop terminates.

A self-closing element, eg. <colour type="rgb" value="255,255,255" />, always produces a Start code immediately followed by an End code, so that calling code can track nesting depth itself without special-casing self-closing elements.

When content is found, R1 points to the data and R2 gives the number of bytes to read; a suitable terminator is always present at the byte immediately following the data, so a short string of content can be read directly as a terminated string. The content may arrive as a single block, or scattered across a series of Content events, depending on the available buffer space and the transport in use; calling code should be able to cope with content arriving in several pieces.

SYS "eXML_ParseBuffer",xml%,xml_buf%,xml_len%

REPEAT
   SYS "eXML_Parse",xml%,0 TO r%,element%,attr%
   CASE r% OF
      WHEN eXML_Parser_Start
         PRINT "S/"+$element%
      WHEN eXML_Parser_End
         PRINT "E/"+$element%
      WHEN eXML_Parser_ERROR
         PRINT "ERROR !!!"
   ENDCASE
UNTIL r% = eXML_Parser_Finished OR r% = eXML_Parser_ERROR

eXML_ContextSWI &576c5
Return the current parse position as a single context string
R0=pointer to workspace
R1=pointer to a separator string, or 0 to use the default separator ("/")
R0=pointer to the context string
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is re-entrant

SWI eXML_Parse can return the current context as an array of element-name pointers (see context array); this call instead builds a single, user-readable context string on demand, useful when Content has just been reported and the calling code wants to know which series of elements it belongs to.

The returned string is terminated with the configured terminator (see Set the character used to terminate strings returned by the module). As with the context array, the name of the element currently being opened or closed is not included, since it is not yet (or no longer) part of the context at that point.

REPEAT
   SYS "eXML_Parse",xml% TO r%,element%,attr%
   SYS "eXML_Context",xml%,"/" TO context$
   PRINT context$
UNTIL eXML_Parser_Finished

eXML_DecodeDTDSWI &576d7
Decode a DOCTYPE declaration into its constituent parts
R0=pointer to workspace
R1=pointer to the DTD line's contents, as returned by SWI eXML_Parse
R5=flags; should be 0 for now
R0=0 if successful, else -> error block
R1=pointer to the document-type (a string containing no spaces)
R2=pointer to the identifier string
R3=pointer to the URL
R4=undefined at present; reserved for future use
R5= Updated flags:
Bit(s)NameMeaning
4PublicClear:The declaration is SYSTEM (local)
Set:The declaration is PUBLIC (external), rather than SYSTEM
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is re-entrant

A well-formed XML document should contain a Document Type Declaration (DTD), usually near the start of the file, so that its structure and syntax can be verified against a set of rules, for example:

<!DOCTYPE svg PUBLIC "-//W3C//SVG//EN" "http://www.w3c.org/dtd/svg.dtd">

Because the syntax of a DTD line is quite free-form - it may, for instance, be spread over several lines - this call decodes it into the useful parts listed above. Document creation code is encouraged to build such lines with SWI eXML_DTD instead of constructing them by hand, as that ensures correct syntax.

There is no support at present for an embedded (in-line) DTD; only a reference to an external DTD file (whether local, SYSTEM, or public, PUBLIC) is decoded. As in-line DTDs are being phased out in favour of XML Schema, it is unlikely that support will be added.

The presence of the referenced DTD file is not checked by eXML: the URL is supplied purely for the benefit of an external XML validation tool, and by convention uses a .dtd filename extension.

eXML_DecodeStringSWI &576df
Convert an XML-escaped string back into plain ASCII text
R0=pointer to the source (escaped) string
R1=pointer to the destination buffer
R0=preserved
R1=pointer to the terminating NULL in the destination buffer
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is re-entrant

Performs the reverse operation to SWI eXML_EncodeString: it copies the source string to the destination, translating each escape sequence (&amp;lt;, &amp;gt;, &amp;amp;, &amp;apos;, &amp;quot;) back into the corresponding ISO Latin-1 character.

eXML_PointerSWI &576e1
Read the parser's current position within the document
R0=pointer to workspace
R1=offset into the buffer (or file) holding the document
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is re-entrant

Added in version 0.36. This call reads the parser's current position, allowing an application to record the start and end points of the content held within an element, so that the sub-content between those points can be parsed or otherwise processed again later.

SWI Calls: Writing an XML File

eXML_OpenSWI &576c6
Create a new XML file and write its XML declaration
R0=pointer to workspace
R1=pointer to the filename
R2=pointer to an attribute array, or 0
R3=pointer to a version string, or 0
R4=pointer to an encoding string, or 0
R5= Flags:
Bit(s)NameMeaning
0NoXMLClear:Write the <?xml?> header as usual
Set:Do not write the <?xml?> header
R0=file handle, or 0 if not successful
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Creates a new XML file ready for further calls (such as SWI eXML_StartElement) to write its content; eXML tracks correct opening and closing of elements, attributes and content as they are written. Call SWI eXML_Close once the file is complete.

As part of opening the file, this call writes the XML declaration as the first line of the file, eg. <?xml version="1.0" encoding="UTF-8"?>. The version and encoding attributes may be supplied directly in R3 and R4 (the common case, since both are more or less mandatory), or as ordinary attributes in the array pointed to by R2 - or a mixture of the two, though R2 may be 0 if only version and encoding are required. R2, R3 and R4 are all optional and should be 0 if not used.

The call returns the handle of the newly opened file (or 0 on failure), so that the caller can track it and close it if an error occurs partway through writing.

SYS "eXML_Open",xml%,"adfs::4.$.TestFile",0,"1.0","UTF-8"

produces:

<?xml version="1.0" encoding="UTF-8"?>

and the following is equivalent:

SYS "eXML_Attribute",xml%,"version","1.0"
SYS "eXML_Attribute",xml%,"encoding","UTF-8" TO attr%
SYS "eXML_Open",xml%,"adfs::4.$.TestFile",attr%

eXML_OpenFileSWI &576db
Alternative spelling for eXML_Open
None
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

This call behaves identically to SWI eXML_Open; see that entry for its registers and use.

eXML_OpenBufferSWI &576dc
Begin a new XML document written to a buffer in memory, rather than a file
R0=pointer to workspace
R1=pointer to a buffer to hold the data; the first word of the buffer must hold its size
R2=pointer to an attribute array, or 0
R3=pointer to a version string, or 0
R4=pointer to an encoding string, or 0
R5= Flags:
Bit(s)NameMeaning
0NoXMLClear:Write the <?xml?> header as usual
Set:Do not write the <?xml?> header
R0=error code (0 if OK)
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Works in the same way as SWI eXML_Open, except that all further output is written into the supplied memory buffer rather than to a file. This is useful, for example, when building a header for streamed (eg. internet) communication that does not need to be saved to disc first.

eXML_CloseSWI &576c7
Close a file previously opened for writing
R0=pointer to workspace
R1=RISC OS filetype to give the file, or 0 to use the currently configured filetype (see Set the RISC OS filetype used for files created by the module)
R0=pointer to the buffer, if SWI eXML_OpenBuffer was used to create the file (otherwise preserved)
R1=number of bytes written, if SWI eXML_OpenBuffer was used to create the file (otherwise preserved)
R2=size of the buffer given in R0, if SWI eXML_OpenBuffer was used to create the file (otherwise preserved)
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Closes the file most recently opened with SWI eXML_Open or SWI eXML_OpenBuffer, and clears the module's internal pointers to it. If it was opened as a file, the filetype given in R1 (or the configured default, Text &FFF, if R1 is 0) is applied. The filetype is only used when writing; it has no effect when reading an existing XML file.

eXML_UpdateBufferSWI &576c8
Open an in-memory XML document for selective update
None
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Not yet implemented.

eXML_UpdateFileSWI &576c9
Open an existing XML file for selective update
R0=pointer to workspace
R1=pointer to the filename
R0=filehandle of the input file, or 0 if not successful
R1=filehandle of the output file, or 0 if not successful
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

In some situations, an application needs to update only part of an existing XML file, for example one that several applications share. This call opens an existing file (creating a new output file alongside it) so that its content can be scanned for a required element; the caller's own data can then be inserted, the original element skipped, and scanning continued. Once opened, call SWI eXML_Update repeatedly until parsing is finished, in the same way as SWI eXML_Parse.

SYS "eXML_Init" TO size%
DIM xml% size%
SYS "eXML_Init",xml%

SYS "eXML_SetOption",xml%,eXML_Option_Terminator,13
SYS "eXML_SetOption",xml%,eXML_Option_Separator,"/"

SYS "eXML_UpdateFile",xml%,"adfs::4.$.MyFile" TO in%,out%

claim% = 0
REPEAT
   SYS "eXML_Update",xml%,,,,,claim% TO r%,,,context$
   claim% = 0
   CASE r% OF
   WHEN eXML_Parser_Start
      CASE context$ OF
      WHEN "/document/chapter/page"
         claim% = 1
      ENDCASE
   WHEN eXML_Parser_ERROR
      PRINT "!!! Parser ERROR !!!"
   ENDCASE
UNTIL r% = eXML_Parser_Finished OR r% = eXML_Parser_ERROR

SYS "eXML_Close",xml%,FileType_XML

eXML_UpdateSWI &576ca
Continue a selective update of an XML file previously opened for update
R0=pointer to workspace
R1=mask; 0 for now
R5= Flags: on the first call, bit 0 must be clear.
Bit(s)NameMeaning
0ClaimClear:Leave the current element unclaimed
Set:Claim the previous element (and everything within it): it will be skipped, and no further elements within it will be reported, until it has closed
1ClaimTagSet:When claiming, claim only the opening tag of the element, so it can be replaced by an updated one
R0=

Return code (a subset of those used by SWI eXML_Parse; only these are returned):

CodeMeaning
1Start - StartElement found
10Finished - parsing has finished
11ERROR - an error occurred during parsing
R1=pointer to the name of the last element
R2=pointer to an attribute array of the last element
R3=pointer to an expanded context string, which - unlike SWI eXML_Parse's context - includes the name of the current element
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Call this SWI repeatedly, after opening a file with SWI eXML_UpdateBuffer or SWI eXML_UpdateFile, until parsing has finished. Once the expanded context string in R3 matches the element you were looking for, set bit 0 of the flags on the next call to claim that element (and everything nested within it): it is skipped, and no further elements within it are reported, until it has closed.

eXML_StartElementSWI &576cb
Write the opening tag of a new element
R0=pointer to workspace
R1=pointer to the element name (optionally prefixed with "/" to mark it self-closing; see below)
R2=pointer to an attribute array, or 0
R3=pointer to a content string, or 0
R5= Flags:
Bit(s)NameMeaning
0EmptyClear:A normal element, requiring a matching SWI eXML_EndElement
Set:This is an empty (self-closing) element; do not call SWI eXML_EndElement for it
1NoLFClear:Insert a line feed at the end of this tag
Set:Don't insert a line feed at the end of this tag
2NoIndentClear:Indent this element with leading spaces, as normal
Set:Don't indent this element (no leading spaces before the tag)
3NewLineClear:Keep all attributes on the same line as the tag
Set:Start each attribute on a new line, to improve readability
R0=0 if successful, else -> error block
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Writes the opening tag of a new element to the file (or buffer) opened by SWI eXML_Open or SWI eXML_OpenBuffer. A StartElement should normally be followed by optional content, optional nested elements, and finally a matching SWI eXML_EndElement.

An element may instead be marked empty (self-closing), in which case it may still hold attributes but cannot hold content or nested elements, and a / is inserted before the tag's closing >, eg. <colour red="255" green="128" blue="44"/>. An element can be marked self-closing in any of three ways: by setting bit 0 of the flags on entry; by prefixing the element name in R1 with /, eg. R1 -> /colour; or by supplying a content string in R3 (see below). In every case, do not call SWI eXML_EndElement for the element - it is closed automatically.

A single short line of text can be inserted as content by pointing R3 at it: the StartElement is written, the content is inserted, and the EndElement is written automatically, so no separate call to SWI eXML_EndElement is required. For example:

SYS "eXML_StartElement",xml%,"comment",attr%,"Just a line of text."

produces:

<comment>Just a line of text.</comment>

If more than one line of text is needed, omit the string in R3 and use SWI eXML_Content between matching SWI eXML_StartElement and SWI eXML_EndElement calls instead.

eXML_EndElementSWI &576cc
Write the closing tag of an element previously opened with eXML_StartElement
R0=pointer to workspace
R1=pointer to the element name, or 0 to close the most recently opened element
R5= Flags:
Bit(s)NameMeaning
1NoLFClear:Insert a line feed after this element, as normal
Set:Don't insert a line feed after this element
2NoIndentClear:Indent as normal (leading spaces before the tag)
Set:Don't indent (no leading spaces before the tag)
R0=0 if successful, else -> error block
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Closes an element previously opened with SWI eXML_StartElement - except an element that was marked self-closing, which must not be closed explicitly. Every other opened element must eventually be closed, and elements must be closed in the correct (nested) order; XML does not permit overlapping elements.

eXML checks the element name given in R1 against its internal stack of open elements, and produces an error if the wrong element is closed. Supplying 0 in R1 closes the most recently opened element without this check, but this is not recommended, since it prevents eXML from verifying that elements are being closed in the correct order.

The following opens and closes correctly:

<document>
   <page id="1">
      <frame>
         <text>Example</text>
      </frame>
   </page>
</document>

whilst this produces an error, because <frame> and <page> close in the wrong order:

<document>
   <page id="1">
      <frame>
         <text>Example</text>
      </page>
   </frame>
</document>

eXML_ContentSWI &576cd
Write content data inside the currently open element
R0=pointer to workspace
R1=pointer to the data
R2=number of bytes to write
R5= Flags:
Bit(s)NameMeaning
1NoLFClear:Insert a line feed after the data, as normal
Set:Don't insert a line feed after the data
2NoIndentClear:Insert leading white space before the output, as normal
Set:Don't insert leading white space before the output
6NoEncClear:Translate reserved characters, as normal
Set:Don't translate reserved (special) characters
R0=0 if successful, else -> error block
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Inserts content inside the element most recently opened with SWI eXML_StartElement. A single short line can also be supplied directly to SWI eXML_StartElement; use this call instead for larger blocks of text, multiple lines, or any number of bytes. Content may contain line feed and/or carriage return characters, and these are preserved (unlike in HTML).

Content may only be written once an element is open; writing content without an open element produces the Error_eXML_FileNotOpen error.

The following places content inside a nested element:

<document>
   <page>
      <frame id="1">This is content</frame>
   </page>
</document>

eXML_AttributeSWI &576ce
Add one attribute to the pending attribute stack
R0=pointer to workspace
R1=pointer to the attribute name
R2=pointer to the attribute value
R0=pointer to an attribute array, or 0 if unsuccessful
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

An attribute takes the form name="value", and an element may carry any number of them. Rather than building an attribute array by hand, call this SWI once for each attribute to be added to the pending stack, then pass the returned pointer to SWI eXML_StartElement, SWI eXML_Open, or SWI eXML_Declaration. The stack is cleared automatically once the element is created, so it rarely needs to be cleared explicitly (see SWI eXML_ClearAttributes).

SYS "eXML_StartElement",xml%,"document"
id$ = "1"
value$ = "date and time"
SYS "eXML_Attribute",xml%,"id",id$ TO attr%
SYS "eXML_Attribute",xml%,"name",value$ TO attr%
SYS "eXML_StartElement",xml%,"field",attr%,"Hello"
SYS "eXML_EndElement",xml%,"document"

produces:

<document>
   <field id="1" name="date and time">Hello</field>
</document>

Note that the field element does not need an explicit SWI eXML_EndElement call, since it was given a content string and so is closed automatically.

eXML_DeclarationSWI &576d0
Write a processing declaration, such as the leading <?xml?> declaration
R0=pointer to workspace
R1=pointer to the declaration name
R2=pointer to an attribute array, or 0
R5=flags
R0=pointer to an attribute array, or 0 if unsuccessful
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Declarations are typically used near the start of a document; the most common is the <?xml?> declaration, which must be present for a document to be considered valid. This call produces output similar to SWI eXML_StartElement, but the line begins with <? and ends with ?>. Declarations do not need to be (and cannot be) closed with SWI eXML_EndElement, and are not considered part of the document body.

The content and processing of a declaration is entirely up to the application reading it; eXML places no further interpretation on it.

<?xat doctype="status" version="4.0"?>

eXML_CommentSWI &576cf
Write a comment
R0=pointer to workspace
R1=pointer to the comment text
R2=length of the string in R1, or 0 to use the entire string up to a control character
R0=0 if successful, else Corrupted
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Writes a comment to the output document, in the standard form <!-- comment text -->.

SYS "eXML_Comment",xml%,"this is a comment"
SYS "eXML_Comment",xml%,"this is a comment",17

eXML_DTDSWI &576d1
Write a DOCTYPE declaration
R0=pointer to workspace
R1=pointer to the document-type string
R2=pointer to an identifier string (optional, PUBLIC only)
R3=pointer to a URL string
R5= Flags:
Bit(s)NameMeaning
2NoIndentClear:Insert white space before this element, as normal
Set:Don't insert white space before this element
4PublicClear:The DTD is SYSTEM (local)
Set:The DTD is PUBLIC, rather than SYSTEM
5IncludeSet:Information from the DTD is included in-line (not currently supported)
R0=0 if successful, else -> error block
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Writes a DOCTYPE line at the start of a document, after the <?xml?> declaration but before the first element. A DTD describes the structure of a document, and the possible values for each attribute, so that a validation tool can check the document against it; it is also used by the XML File Dispatcher to identify a document's type when it is double-clicked.

There are three kinds of DTD. A SYSTEM DTD is stored locally, and needs only a relative URL to the DTD file in R3. A PUBLIC DTD is stored externally (for example on the internet); it needs a URL in R3 and an identifier string in R2 describing the publisher, format and language of the document, and is selected by setting bit 4 of the flags. An in-line DTD embeds its information directly in the DOCTYPE declaration, but this is not currently supported by eXML.

The document-type in R1 identifies the kind of document, eg. svg; where an application uses several document types, including the application's own name as a prefix, eg. artworks:svg, allows an unlimited number of distinct types to be created without clashing with anyone else's.

The identifier in R2 (PUBLIC DTDs only) should describe the publisher, the document format and version, and the language, separated by double forward-slashes, eg. -//XAT//CableNews Edition 4.0//EN. A leading - indicates a publisher that is not ISO certified; a leading + indicates one that is. A SYSTEM DTD has no identifier; if one is supplied it is ignored, so that XML checkers and browsers do not report an error over it.

The URL in R3 is not used by eXML's own parser: it exists purely for an external XML validation tool. For a SYSTEM DTD, use a relative path, eg. edition.dtd or files/edition.dtd; for a PUBLIC DTD, use an absolute URL, eg. http://www.xat.nl/dtd/cablenews/edition.dtd. In both cases the filename should carry a .dtd extension, and the path should be given in URL form rather than RISC OS pathname form.

SYS "eXML_DTD",xml%,"edition",,"files/edition.dtd"

produces a SYSTEM DTD:

<!DOCTYPE edition SYSTEM "files/edition.dtd">

whereas:

id$ = "-//XAT//Edition 4.0//EN"
url$ = "http://www.xat.nl/dtd/edition.dtd"
SYS "eXML_DTD",xml%,"edition",id$,url$,,(1 << 4)

produces a PUBLIC DTD:

<!DOCTYPE edition PUBLIC "-//XAT//Edition 4.0//EN" "http://www.xat.nl/dtd/edition.dtd">

eXML_PISWI &576d2
Write a Processing Instruction
R0=pointer to workspace
R1=pointer to the data
R2=number of bytes to insert
R5=flags
R0=0 if successful, else -> error block
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

Inserts a Processing Instruction into the output document, used to pass information directly to whatever XML processor reads the document (for example, the parser inside your own application). Processing instructions are not considered part of the document body, and their syntax is not checked by eXML. Only the opening (<?) and closing (?>) delimiters are added; the data in R1 is inserted between them exactly as supplied.

$data% = "xat:impression validation extra"
size% = LEN($data%) + 1
SYS "eXML_PI",xml%,data%,size%

produces:

<?xat:impression validation extra?>

eXML_CDATASWI &576d3
Write a CDATA (raw data) section
R0=pointer to workspace
R1=pointer to the data
R2=number of bytes to insert
R5=flags
R0=0 if successful, else -> error block
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is not re-entrant

The XML standard allows raw data - including reserved characters such as < and > - to be inserted into a document without escaping, inside a CDATA section. The only sequence a CDATA section cannot contain is ]]>, which marks its end. A CDATA section is opened with <![CDATA[ and closed with ]]>.

Reading CDATA sections is not currently supported by eXML.

SWI Calls: Universal Calls

eXML_GetStringSWI &576d4
Copy a control-terminated string, applying a chosen terminator
R0=pointer to the original string
R1=pointer to the destination, somewhere in the caller's own workspace
R2=required string terminator, eg. 0 or 13
R1=pointer to the terminating character written into the destination string
R2=length of the destination string, excluding its terminator
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is re-entrant

This call is not really specific to XML, but is provided because it is useful for building structures (such as large menus) from data received elsewhere, for example a WIMP message. It copies a string terminated by any control character into the caller's own buffer, applying whichever terminator the caller's software requires.

The following, in BBC BASIC, extracts the document-type and path from a Message_XMLDataOpen message block:

DIM mes 256
DIM par% 256
...
SYS "eXML_GetString",mes!40,par%,13
doctype$ = $par%
SYS "eXML_GetString",mes+44,par%,13
path$ = $par%

eXML_ColourOpSWI &576d6
Read, write, and convert between colour formats
R0=

Reason code:

ReasonAction
0Initialise a colour data block, or find its required size
1Read a colour from a text string into a colour data block
2Write a colour data block out as a formatted text string
3Convert a colour string directly into a RISC OS colour value
4Convert a RISC OS colour value directly into a formatted text string
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is re-entrant

There are many ways to express a colour in an XML document - some inherited from HTML, some specific to XML - and converting between them can be tedious, especially when the user should be free to choose their preferred colour model. SWI eXML_ColourOp converts between these formats, and reads or writes colours directly. Every reason code operates on a colour data block held in the caller's own memory (except reason codes 3 and 4, which use the module's own internal workspace instead - see below). R0 selects the operation; the meaning of the other registers depends on the reason code.

It is recommended that colour be expressed as an attribute on an existing element rather than as an element of its own, eg. <background colour="rgb(255,255,255)"/> rather than <background><colour value="rgb(255,255,255)"/></background>.

eXML_ColourOp 0InitSWI &576d6
Initialise a colour data block, or find its required size
R1=pointer to a colour data block, or 0 to find the required size
R5= Flags:
Bit(s)NameMeaning
0-7TerminatorRequired string terminator, eg. 0 or 13
R2=size of the colour data block, in bytes, when R1 was 0 on entry
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

Before a colour data block is used with any other reason code, it should be set to a defined state by calling this reason code with a pointer to it in R1. To find the required size of the block, call with a null pointer in R1; the size is returned in R2. The size should always be requested first, since it may grow in a future version of the module.

eXML_ColourOp 1ReadSWI &576d6
Read a colour from a text string into a colour data block
R1=pointer to a colour data block
R2=pointer to a text string containing a colour definition
R5=flags; 0 for now
R0preserved
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

Parses the colour definition string in R2, in any of the formats described under colour data block, and updates the block pointed to by R1 with the resulting colour, colour model, and format.

eXML_ColourOp 2WriteSWI &576d6
Write a colour data block out as a formatted text string
R1=pointer to a colour data block
R2=pointer to a buffer to hold the resulting string
R5= Flags:
Bit(s)NameMeaning
0-7TerminatorRequired string terminator, eg. 0 or 13
8-15FormatRequired output colour format (see colour data block); 0 to use the format already recorded in the block
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

Formats the colour held in the data block pointed to by R1 as text, in either the colour's original format or the format requested in bits 8-15 of the flags, and writes it to the buffer in R2. This is typically used directly to produce XML output.

SYS "eXML_ColourOp",0,0 TO ,,size%
DIM block% size%
DIM string% 256

SYS "eXML_ColourOp",0,block%

SYS "eXML_ColourOp",2,block%,string%,,,13 OR (2 << 8)
PRINT $string%

SYS "eXML_ColourOp",1,block%,string%
PRINT ~block%!12

eXML_ColourOp 3GetSWI &576d6
Convert a colour string directly into a RISC OS colour value
R1=pointer to a formatted colour text string
R0=the colour, as a RISC OS colour value
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

Equivalent to Read a colour from a text string into a colour data block, but uses the module's own internal workspace for the conversion rather than a caller-supplied colour data block, so no block needs to be created first.

SYS "eXML_ColourOp",3,"rgb(255,127,64)" TO colour%

eXML_ColourOp 4PutSWI &576d6
Convert a RISC OS colour value directly into a formatted text string
R1=colour value, in RISC OS format
R5=flags, as for SWI eXML_ColourOp 2
R0=pointer to the resulting string
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

Equivalent to Write a colour data block out as a formatted text string, but uses the module's own internal workspace for the conversion rather than a caller-supplied colour data block.

SYS "eXML_ColourOp",4,&4682FF00,,,,&020D TO colour$
PRINT colour$

produces:

rgb(255,130,70)

eXML_DateOpSWI &576dd
Read (and, in future, write) dates and times in various formats
R0=

Format to read (or, when writing, to produce):

FormatMeaning
0Auto - detect the format automatically
1XML - year-month-date, separated by dashes, eg. 2016-12-31
2EUR - date-month-year, separated by dashes, eg. 31-12-2016
3USA - month/date/year, separated by slashes, eg. 12/31/2016
R1=

When writing (not yet implemented), further flags select what is written:

Bit(s)NameMeaning
16WriteSet:Write a date (rather than read one)
17DateSet:Include the date
18TimeSet:Include the time
19SecSet:Include seconds
R0=
ValueMeaning
0No error
1Wrong format
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is re-entrant

The official XML standard specifies dates in reverse (year-month-date) order, separated by dashes, eg. 2014-06-31, optionally followed by a time, separated by an optional space and the letter T, eg. 2014-06-31 T14:23:01. The part following T gives hours, minutes and seconds, each separated by a colon.

Reading is implemented: this call reads an existing date (and optional time) from a text string, either in a specific format selected in R0, or automatically (format 0), in which case XML and EUR dates are assumed to be separated by dashes and USA dates by slashes. Writing a date is not yet implemented.

eXML_EncodeStringSWI &576de
Copy a NULL-terminated string, escaping reserved XML characters
R0=pointer to the source string
R1=pointer to the destination buffer
R0=preserved
R1=pointer to the terminating NULL in the destination buffer
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is re-entrant

Copies a NULL-terminated string from the source in R0 to the destination in R1, translating any of the reserved characters (<, >, &amp;, ', ") into the corresponding XML escape sequence (&amp;lt;, &amp;gt;, &amp;amp;, &amp;apos;, &amp;quot;).

eXML_Base64SWI &576e0
Encode or decode Base64 data
None
None
Interrupts are undefined
Fast interrupts are undefined
Processor is in SVC mode
SWI is re-entrant

Not implemented.

The eXML module distributed as version 0.43 (13 Jun 2020) - one point release after this API document - also occupies four further SWI numbers in its decoding table which this specification does not describe: eXML_GetStack (&576D9), eXML_ConvertToUTF8 (&576DA), eXML_DecodeBlock (&576E2), and eXML_Debug (&576E3). Their names are known from the module's own SWI decoding table, but their calling conventions are not published anywhere, so they are not documented as full SWI entries here.

XML File Dispatcher

Most files on RISC OS carry a filetype, which lets an application recognise its own files and respond when the Filer reports that one has been double-clicked; if no application claims a filetype, the Filer falls back to a system variable such as Alias$@RunType_FFF to launch something suitable.

XML makes it easy to create many new file formats, but RISC OS filetypes are a scarce resource (numbers &000 to &FFF only), and typically only one filetype will be allocated to a given application, which must then inspect each file's contents to work out how to use it. By default, an XML file with no more specific filetype allocated to it should use RISC OS filetype &F80. Although every such file then looks identical to the Filer, its document-type can still be recovered from its DOCTYPE line, eg.

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE MyDoc SYSTEM "mydoc.dtd">
<document>
   <chapter id="1">
      <page id="1">
         <frame>Hello world...</frame>
      </page>
   </chapter>
</document>

Here, the document-type (referred to below as <doctype>) is MyDoc. A document-type may be any length and contain any printable XML character except a space; conventionally it uses only letters and, perhaps, numbers, eg. artworks, svg, cablenews, config. An application handling several distinct document-types of its own may prefix them with a family name and a colon, eg. artworks:config, but should only do this for a format that is intended to remain private, rather than public - see also XML File Dispatcher: Naming Conventions below.

For any of this to work, the file must carry RISC OS filetype &F80. Double-clicking such a file is intercepted by eXML, which extracts the document-type from its DOCTYPE line and then either broadcasts a WIMP message, or launches a suitable application, as described below.

XML File Dispatcher: System Variables

eXML uses system variables of the following form to decide what to launch when an XML file is opened:

XML$@Run_<doctype>
Command used to launch an application for a given XML document-type

An application which wants to be launched automatically whenever the user double-clicks a file of a given <doctype> should set this variable, replacing <doctype> with the actual document-type it handles, to a command that launches it with the file's pathname in its command tail:

Set XML$@Run_svg Run <ArtWorks$Dir>.!Run %%**0

Note the doubled %, which is required to escape the literal % that follows it. Whenever the user double-clicks an XML file (RISC OS filetype &F80) with document-type svg, and ArtWorks is not already running, eXML launches it as configured, substituting the file's pathname for %*0 in the command tail.

If no XML$@Run_<doctype> variable exists for a file's document-type, and no running application claims the Message_XMLDataOpen message described below, the file cannot be opened automatically. Every other system variable beginning XML$@ is reserved for future use by eXML.

XML File Dispatcher: Messages

Before falling back to the XML$@Run_<doctype> system variable, the dispatcher broadcasts a User_Message (18) of type Message_XMLDataOpen, in a format similar to the standard Message_DataOpen (5) broadcast by the Filer when any file is double-clicked. The only difference is that the field at offset +40 holds a pointer to a string giving the document-type, rather than the filetype.

Message_XMLDataOpen&40D59
A user has double-clicked an XML file of the given document-type
OffsetSizeNameContents
04sizeSize of the message block
44senderTask handle of the sender
84my_refThe sender's reference for this message
124your_ref0
164action&40D59 (Message_XMLDataOpen)
204window_handleWindow handle of the directory display containing the file
244Reserved, must be zero
284icon_xX offset of the file icon that was double-clicked
324icon_yY offset of the file icon that was double-clicked
3640
404doctypePointer to a zero-terminated string giving the document-type
44?pathnameFull pathname of the file, zero-terminated
Message must be sent directly to task

Message may only be sent normally (reason code 17)

Any application that can load a file of the document-type given at offset +40 should respond by first claiming the message - by sending a Message_XMLDataOpenAck reply, with your_ref set to the original message's my_ref - and only then loading the file. Claiming first, before attempting to load, avoids the message being passed on to another application if the load subsequently fails.

DIM mes 256
...
REPEAT
   SYS "Wimp_Poll", 0, mes TO r%
   CASE r% OF
   WHEN 17, 18, 19 : PROC_message
   ENDCASE
UNTIL quit

DEF PROC_message
   CASE mes!16 OF
   WHEN &40D59 : PROC_xml_dataopen
   ENDCASE
ENDPROC

DEF PROC_xml_dataopen
   doctype$ = FN_read_string(mes!40)
   file$ = FNread_string(mes+44)
   CASE doctype$ OF
   WHEN "svg"
      mes!16 = &40D5A
      mes!12 = mes!8
      SYS "Wimp_SendMessage",17,mes,mes!4
      REM ---> load the file
      ...
   ENDCASE
ENDPROC

Message_XMLDataOpenAck&40D5A
Acknowledges and claims a XMLDataOpen message
No additional data
Message must be sent directly to task

Message may only be sent normally (reason code 17)

Sent as a User_Message (17) reply to Message_XMLDataOpen, with your_ref copied from the original message's my_ref, to claim the file before loading it.

XML File Dispatcher: Naming Conventions

Choose a document-type carefully, to avoid clashing with another application's document-type when a file is loaded automatically. Prefer a name qualified by your own application or company, such as MyApp:MyDoc or MyCompany:MyDoc, over an unqualified name such as MyDoc. Some examples: artworks, xat:database, dbman:users, xat:dbman:database.

Error Messages

Error_eXML_FileNotOpenError &81CD00
A call that writes to, or reads the position of, an XML file was made with no file currently open

Returned by any call that writes content, elements, or other output when no file (or buffer) is currently open for writing, for example calling SWI eXML_Content before SWI eXML_Open or SWI eXML_OpenBuffer.

Error_eXML_NoElementError &81CD01
An operation on an element was requested, but no element is currently open

Returned, for example, by SWI eXML_EndElement when there is no corresponding open element left to close.

Error_eXML_NoEndError &81CD02
More elements were closed than have been opened

Returned by SWI eXML_EndElement when trying to close an element after every currently open element has already been closed.

Error_eXML_WrongEndError &81CD03
An element was closed with a name that does not match the most recently opened element

Returned by SWI eXML_EndElement when the element name given in R1 does not match the element eXML expects to be closed next, according to its internal stack of open elements - for example, closing elements in the wrong (non-nested) order.

Error_eXML_NoEncodingError &81CD04
The requested character encoding is not known, or is not supported by this version of the module

Returned by SWI eXML_Parse (as the Encoding return code) when a document requests an encoding outside the set listed under Encodings, or one that is not yet implemented in the running version of the module.

Error_eXML_NoDocTypeError &81CD05
An operation on a DOCTYPE declaration was requested, but no document-type could be determined

Returned when a document-type cannot be established from a DOCTYPE declaration, for example when dispatching a double-clicked XML file, or when decoding a malformed DTD line with SWI eXML_DecodeDTD.

Error_eXML_DespatchError &81CD06
The XML File Dispatcher could not dispatch a double-clicked file

Returned when the XML File Dispatcher cannot dispatch a double-clicked XML file - for example, no application claimed the Message_XMLDataOpen message, and no matching XML$@Run_<doctype> system variable was set.

Error_eXML_WorkspaceError &81CD07
The workspace pointer supplied in R0 is not valid, or has not been initialised

Returned when a call is made with a workspace pointer that eXML does not recognise as having been initialised by SWI eXML_Init or SWI eXML_InitBuffer.

Error_eXML_BufferError &81CD08
No content buffer is available, or the supplied buffer pointer is not valid

Returned when a call needs the workspace's Content buffer (see SWI eXML_InitBuffer), but none is available, or when a caller-supplied buffer pointer, for example to SWI eXML_OpenBuffer, is not valid.

Error_eXML_BufferSizeError &81CD09
The supplied buffer is too small for the data being read or written

Returned when a caller-supplied buffer, such as the Content buffer sized by SWI eXML_InitBuffer, or the buffer given to SWI eXML_OpenBuffer, is too small to hold the data eXML needs to read or write into it.

Error_eXML_ColourUnknownError &81CD10
A colour string uses a format that is not recognised

Returned by SWI eXML_ColourOp 1 (or reason 3) when the text supplied does not match any of the colour formats described under colour data block.

Error_eXML_ColourSyntaxError &81CD11
A colour string is in a recognised format, but is not syntactically valid

Returned by SWI eXML_ColourOp 1 (or reason 3) when the text supplied is recognisably one of the supported colour formats, but its syntax is malformed - for example, a missing value or an unmatched bracket.

Error_eXML_ColourFormatError &81CD12
The requested output colour format is not supported for writing

Returned by SWI eXML_ColourOp 2 (or reason 4) when the colour format requested in the flags is one that the module does not yet support producing, such as YUV or YUV%.

Error_eXML_ColourValueError &81CD13
A colour value cannot be converted into the requested format or model

Returned by SWI eXML_ColourOp 2 (or reason 4) when the stored colour value cannot be represented in the requested output format, for example because it falls outside the range that format can express.