Domino Canonical Format
Chapter 15-1
Domino Canonical Format
Introduction
C API programs that run on non-Intel based platforms such as Macintosh and UNIX must convert certain types of data between host-specific format and Domino canonical format when accessing item data. This chapter explains when and how to perform this conversion.
C API programs for Windows do not need to perform host/canonical conversion. However, all API programs should perform host/canonical conversion because it makes the code more portable and does not increase the complexity of the code significantly. API programs for Windows can perform host/canonical conversion with no adverse side effects.
Audience
You should read this chapter if:
- You develop or plan to develop API programs for any platform other than Windows
- You are porting or plan to port API programs from OS/2 or Windows to any non-Intel platform, such as Unix.
- You would like to make your API programs portable to other platforms.
Domino Canonical Format
Domino canonical format is defined as Intel 8086 byte ordering with no pad bytes.
Domino and Notes store all data on disk in Domino canonical format. Domino and Notes also transmit all data over network communications links in Domino canonical format. This way, Domino running on one platform can communicate transparently with Domino or Notes running on a different platform. Since all NSF files have the same binary format regardless of the platform on which the file was created, a UNIX server (for example) can read an .NSF file created on a Windows workstation.
Generally, Domino insulates API programs from the details of canonical format. However, for certain data types, portable API programs must convert from host to canonical format before writing data to a Domino database and convert from canonical to host format after reading data from a Domino database.
Data Types that Require Conversion
The following data types require conversion:
TYPE_COMPOSITE
TYPE_COLLATION
TYPE_OBJECT
TYPE_VIEW_FORMAT
TYPE_ICON
TYPE_SIGNATURE
TYPE_SEAL
TYPE_SEALDATA
TYPE_SEAL_LIST
TYPE_WORKSHEET_DATA
TYPE_USERDATA
TYPE_QUERY
TYPE_ACTION
TYPE_ASSISTANT_INFO
TYPE_VIEWMAP_DATASET
TYPE_VIEWMAP_LAYOUT
TYPE_CALENDAR_FORMAT
For all other data types, the NSF subsystem performs the conversion automatically.
For example, let's say that the following structure is to be written out to an item of TYPE_COMPOSITE:
typedef struct {
WORD mem1;
DWORD mem2;
} CDSTRUCT;
...
CDSTRUCT DNStruct;
On a UNIX, the mem2 member of DNStruct is aligned on a DWORD boundary. If you do not convert this structure to canonical format when copying it to the item buffer that is to be written to disk, then when Domino or Notes attempts to read this item, there will be two nonsense bytes where Domino expects to find the mem2 data.
When to Convert
Given item data of one of the above types:
- Portable API programs must convert the item data to canonical format before calling NSFItemAppend or any function that appends item data to a note.
- After calling NSFItemInfo or any equivalent function to read item data from a note, the API program must convert the item data from canonical format to host format before accessing the item data.
API programs generally do not need to perform host/canonical conversion when using higher-level functions such as NSFItemGetText, NSFItemSetText, or NSFItemGetNumber, which do not manipulate any of the data types that require conversion. Conversion is also unnecessary when using any of the "Easy" rich text routines, such as CompoundTextAddText.
Do not convert data stored in an item that is not one of the aforementioned data types. For example, do not convert a LIST structure stored in an item of TYPE_NOTELINK. Do convert a LIST structure stored in an item of TYPE_COMPOSITE.
How to Convert
The API provides three functions that allow API programs to convert data between host-specific format and Domino canonical format:
- ODSReadMemory - Converts from Domino canonical format to host-specific format
- ODSWriteMemory - Converts from host-specific format to Domino canonical format
- ODSLength - Gets the length, in bytes, of a data structure in Domino canonical format
ODSReadMemory converts Domino data structures from Domino canonical format to machine-specific format and writes the results into a variable or memory buffer provided by the caller. Portable API programs use ODSReadMemory after reading item data of one of the types listed above and before accessing that data.
ODSWriteMemory converts Domino data structures from machine-specific format to Domino Canonical format and writes the results into a memory buffer provided by the caller. Portable API programs use ODSWriteMemory to prepare item data of one of the types listed above before appending the data to a note.
ODSLength returns the length, in bytes, of a data structure in Domino canonical format. Use this function when calculating the buffer size needed to contain data structures converted to Domino canonical format and when initializing the Length members of CD record header structures.
NOTE: These ODS functions are supported on all platforms. We recommend that you include these functions in the appropriate places in all API code to ensure that the code is platform-independent.
The HCL C API for Domino and Notes Reference provides detailed information about these functions.
These ODS functions perform different actions depending on the platform on which the API program is running. If your API program calls ODS functions in the proper places, it will be portable to both Intel and non-Intel architecture machines. On Intel architecture machines, these ODS functions are not strictly necessary, but if used properly will do no harm.
Example Using ODSWriteMemory
The sample API program DYNAMIC shows how to use ODSWriteMemory to convert Domino data structures from host format to canonical format.
The code fragment below adds a rich text field to a note. It creates the rich text data by setting up a buffer that consists of a series of CD records.
In order to create each CD record, DYNAMIC needs certain data structures. For example, it needs a variable of type CDPARAGRAPH. It defines these data structures as local variables. Note that when initializing the data structures, DYNAMIC sets the Length member of the record header to the ODS length of the resulting CD record. For example,
- para.Header.Length = (BYTE) ODSLength( _CDPARAGRAPH );
After initializing each data structure, it converts the data structure from host format to Domino canonical format when appending it to the buffer. The resulting buffer is entirely in Domino canonical format. The code then creates a rich text field by calling NSFItemAppend to append the buffer to a note.
Example Using ODSWriteMemory |
BYTE rt_field; / allocated rich-text field /
BYTE buff_ptr; / position in allocated memory /
CDPABDEFINITION pabdef; / rich-text paragraph style /
CDPARAGRAPH para; / rich-text paragraph header /
CDPABREFERENCE ref; / rich-text style reference /
CDTEXT cdtext; / rich-text text header /
char szString1[] = "Hello world... ";
char szString2[] = "So long world ";
WORD wString1Len = strlen( szString1 );
WORD wString2Len = strlen( szString2 );
FONTIDFIELDS pFontID; / font definitions in text header /
DWORD rt_size; / size of rich-text field /
/ ... steps missing ... /
rt_field = (BYTE ) malloc ( wBuffLen );
/ Keep a pointer to our current position in the buffer. /
buff_ptr = rt_field;
/ Initialize a CDPABDEFINITION structure./
pabdef.Header.Signature = SIG_CD_PABDEFINITION;
pabdef.Header.Length = ODSLength( _CDPABDEFINITION );
pabdef.PABID = PARA_STYLE_ID;
pabdef.JustifyMode = JUSTIFY_CENTER;
pabdef.LineSpacing = DEFAULT_LINE_SPACING;
pabdef.ParagraphSpacingBefore = DEFAULT_ABOVE_PAR_SPACING;
pabdef.ParagraphSpacingAfter = DEFAULT_BELOW_PAR_SPACING;
pabdef.LeftMargin = DEFAULT_LEFT_MARGIN;
pabdef.RightMargin = DEFAULT_RIGHT_MARGIN;
pabdef.FirstLineLeftMargin = DEFAULT_FIRST_LEFT_MARGIN;
pabdef.Tabs = DEFAULT_TABS;
pabdef.Tab[0] = DEFAULT_TAB_INTERVAL;
pabdef.Flags = 0;
/ Call ODSWriteMemory to convert the CDPABDEFINITION structure to
Domino canonical format and write the converted structure into
the buffer at location buff_ptr. This advances buff_ptr to the
next byte in the buffer after the canonical format strucure.
/
ODSWriteMemory( &buff_ptr, _CDPABDEFINITION, &pabdef, 1 );
/ Put a paragraph header in the field. /
para.Header.Signature = SIG_CD_PARAGRAPH;
para.Header.Length = (BYTE) ODSLength( _CDPARAGRAPH );
ODSWriteMemory( &buff_ptr, _CDPARAGRAPH, ¶, 1 );
/ Put a paragraph reference block in the field./
ref.Header.Signature = SIG_CD_PABREFERENCE;
ref.Header.Length = (BYTE) ODSLength( _CDPABREFERENCE );
ref.PABID = PARA_STYLE_ID;
ODSWriteMemory( &buff_ptr, _CDPABREFERENCE, &ref, 1 );
/ Add the CDTEXT record to the field. A CDTEXT record consists
of a CDTEXT structure followed by a run of text. Initialize the
CDTEXT structure by filling in the signature and the length.
The CDTEXT structure also contains the font information that
controls how Domino displays this first run of text.
/
cdtext.Header.Signature = SIG_CD_TEXT;
cdtext.Header.Length = ODSLength( _CDTEXT ) + wString1Len ;
pFontID = (FONTIDFIELDS ) &(cdtext.FontID);
pFontID->Face = FONT_FACE_SWISS;
pFontID->Attrib = ISBOLD;
pFontID->Color = FONT_COLOR_BLUE;
pFontID->PointSize = 24;
ODSWriteMemory( &buff_ptr, _CDTEXT, &cdtext, 1 );
/ Write the actual characters of this first text run to the buffer.
Since the run of text may contain embedded null characters, use
memcpy, not strcpy. No need to terminate the run of text with a
null because the Header.Length member of the CDTEXT structure
specifies the length explicitly.
/
memcpy( (char )buff_ptr, szString1, wString1Len );
buff_ptr += wString1Len;
/ We are done filling the buffer with CD records. Now append the
buffer to the note as a rich text field. First find the size of
the buffer. Then add the rich-text field to the note by calling
NSFItemAppend. NSFItemAppend copies the data out of the buffer
specified by rt_field. Therfore, after calling NSFItemAppend, we
can free the buffer.
/
rt_size = (DWORD)(buff_ptr - rt_field);
error = NSFItemAppend( note_handle,
0,
"RICH_TEXT", strlen("RICH_TEXT"),
TYPE_COMPOSITE,
rt_field, rt_size );
Example Using ODSReadMemory
The sample API program DUMPFONT shows how to use ODSReadMemory to convert Domino data structures from canonical format to host format. The code fragment below uses ODSReadMemory to convert a font table from canonical format to host format before printing the contents of the font table to the screen.
A font table is an item of TYPE_COMPOSITE. It consists of a CDFONTTABLE structure followed by one or more CDFACE structures. When an API program reads a CDFONTTABLE or CDFACE structure from the NSF subsystem, these structures are in canonical format. API programs must convert these canonical format data structures to host-specific format before attempting to access the members of the structures. The code below uses ODSReadMemory to perform this conversion.
Example Using ODSReadMemory |
/**********
FUNCTION: ProcessOneNote
**********/
STATUS LNPUBLIC ProcessOneNote( void * phDB, DWORD NoteID )
{
DBHANDLE hDB;
STATUS error;
NOTEHANDLE hNote;
BLOCKID bhThisItem;
WORD wType;
BLOCKID bhValue;
DWORD dwLength;
hDB = ( (DBHANDLE )phDB );
if (error = NSFNoteOpen( hDB, NoteID, 0, &hNote))
{
printf( "Error: unable to open note.\n" );
return( ERR(error) );
}
/ Look for the "$Fonts" field within this note. /
error = NSFItemInfo( hNote, ITEM_NAME_FONTS,
strlen (ITEM_NAME_FONTS),
&bhThisItem, / Item BLOCKID /
&wType, / Type /
&bhValue, / Value BLOCKID /
&dwLength); / Value length /
/ If no font table, just return./
if (ERR(error) == ERR_ITEM_NOT_FOUND)
{
NSFNoteClose( hNote );
return NOERROR;
}
else if (error)
{
printf( "Error: unable to access item %d.\n", ITEM_NAME_FONTS );
NSFNoteClose( hNote );
return( ERR(error) );
}
EnumCompositeBuffer( bhValue, dwLength, PrintFontTable, NULL);
NSFNoteClose( hNote );
return NOERROR;
}
/**********
FUNCTION: PrintFontTable
**********/
STATUS LNPUBLIC PrintFontTable( char RecordPtr,
WORD RecordType,
DWORD RecordLength,
void Unused )
{
void pItemValue;
CDFONTTABLE cdFontTab;
CDFACE cdFace;
WORD wIndex;
if (RecordType != SIG_CD_FONTTABLE)
{
/ EnumCompositeBuffer found a CD record in the "$Fonts" field
that is not of type SIG_CD_FONTTABLE. Unusual, but not fatal.
/
return NOERROR;
}
/ RecordPtr points to the item value in canonical format after the
datatype word. The item value starts with a CDFONTTABLE structure.
Call ODSReadMemory to convert this CDFONTTABLE to host format and
store it in cdFontTab. ODSReadMemory increments pItemValue to
point to the next byte in the input buffer after the CDFONTTABLE.
*/
pItemValue = RecordPtr;
ODSReadMemory( &pItemValue, _CDFONTTABLE, &cdFontTab, 1 );
for (wIndex = 0; wIndex < cdFontTab.Fonts; wIndex++)
{
ODSReadMemory( &pItemValue, _CDFACE, &cdFace, 1 );
printf( " Font %d:\n", wIndex );
printf( " Face = %d\n", cdFace.Face );
printf( " Family = %d\n", cdFace.Family );
printf( " Name = %s\n", cdFace.Name );
}
return (NOERROR);
}
Side-by-Side Comparison
The code fragments below show how part of the sample program DYNAMIC was modified in porting from Windows to Unix. The fragment on the left works only on Intel architecture platforms such as Windows. The fragment on the right is portable. It works on all platforms supported by Domino or Notes.
Example: DYNAMIC before porting to UNIX | Example: DYNAMIC after porting to UNIX |
BYTE *rt_field; BYTE *buff_ptr; CDPABDEFINITION *def; /* ... steps missing... */ /* Add a CDPABDEFINITION.*/ def = (CDPABDEFINITION *) buff_ptr; def->Header.Signature = SIG_CD_PABDEFINITION; def->Header.Length = sizeof(CDPABDEFINITION); def->PABID = 1; def->JustifyMode = JUSTIFY_CENTER; def->LineSpacing = DEFAULT_LINE_SPACING; def->ParagraphSpacingBefore = DEFAULT_ABOVE_PAR_SPACING; def->ParagraphSpacingAfter = DEFAULT_BELOW_PAR_SPACING; def->LeftMargin = DEFAULT_LEFT_MARGIN; def->RightMargin = DEFAULT_RIGHT_MARGIN; def->FirstLineLeftMargin = DEFAULT_FIRST_LEFT_MARGIN; def->Tabs = DEFAULT_TABS; def->Tab[0] = DEFAULT_TAB_INTERVAL; def->Flags = 0; /* Advance the buffer pointer, so we know where to put the next object in the buffer. */ buff_ptr += sizeof(CDPABDEFINITION); | BYTE *rt_field; BYTE *buff_ptr; CDPABDEFINITION pabdef; /* .....steps missing ....*/ /* Init a CDPABDEFINITION*/ pabdef.Header.Signature = SIG_CD_PABDEFINITION; pabdef.Header.Length = ODSLength( _CDPABDEFINITION ); pabdef.PABID = PARA_STYLE_ID; pabdef.JustifyMode = JUSTIFY_CENTER; pabdef.LineSpacing = DEFAULT_LINE_SPACING; pabdef.ParagraphSpacingBefore = DEFAULT_ABOVE_PAR_SPACING; pabdef.ParagraphSpacingAfter = DEFAULT_BELOW_PAR_SPACING; pabdef.LeftMargin = DEFAULT_LEFT_MARGIN; pabdef.RightMargin = DEFAULT_RIGHT_MARGIN; pabdef.FirstLineLeftMargin = DEFAULT_FIRST_LEFT_MARGIN; pabdef.Tabs = DEFAULT_TABS; pabdef.Tab[0] = DEFAULT_TAB_INTERVAL; pabdef.Flags = 0; /* Call ODSWriteMemory to convert the CDPABDEFINITION structure to Domino canonical format and write the converted structure into the buffer at location buff_ptr. This advances buff_ptr to the next byte in the buffer after the canonical format structure. */ ODSWriteMemory( &buff_ptr, _CDPABDEFINITION, &pabdef, 1 ); |